sysctl: Refactor the binary sysctl handling to remove duplicate code
Read in the binary sysctl path once, instead of reread it from user space each time the code needs to access a path element. The deprecated sysctl warning is moved to do_sysctl so that the compat_sysctl entries syscalls will also warn. The return of -ENOSYS when !CONFIG_SYSCTL_SYSCALL is moved to binary_sysctl. Always leaving a do_sysctl available that handles !CONFIG_SYSCTL_SYSCALL and printing the deprecated sysctl warning allows for a single defitition of the sysctl syscall. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
This commit is contained in:
parent
afa588b265
commit
2830b68361
|
@ -14,8 +14,6 @@
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
#include <linux/smp_lock.h>
|
#include <linux/smp_lock.h>
|
||||||
|
|
||||||
static int deprecated_sysctl_warning(struct __sysctl_args *args);
|
|
||||||
|
|
||||||
#ifdef CONFIG_SYSCTL_SYSCALL
|
#ifdef CONFIG_SYSCTL_SYSCALL
|
||||||
|
|
||||||
/* Perform the actual read/write of a sysctl table entry. */
|
/* Perform the actual read/write of a sysctl table entry. */
|
||||||
|
@ -51,7 +49,7 @@ static int do_sysctl_strategy(struct ctl_table_root *root,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_table(int __user *name, int nlen,
|
static int parse_table(const int *name, int nlen,
|
||||||
void __user *oldval, size_t __user *oldlenp,
|
void __user *oldval, size_t __user *oldlenp,
|
||||||
void __user *newval, size_t newlen,
|
void __user *newval, size_t newlen,
|
||||||
struct ctl_table_root *root,
|
struct ctl_table_root *root,
|
||||||
|
@ -61,8 +59,7 @@ static int parse_table(int __user *name, int nlen,
|
||||||
repeat:
|
repeat:
|
||||||
if (!nlen)
|
if (!nlen)
|
||||||
return -ENOTDIR;
|
return -ENOTDIR;
|
||||||
if (get_user(n, name))
|
n = *name;
|
||||||
return -EFAULT;
|
|
||||||
for ( ; table->ctl_name || table->procname; table++) {
|
for ( ; table->ctl_name || table->procname; table++) {
|
||||||
if (!table->ctl_name)
|
if (!table->ctl_name)
|
||||||
continue;
|
continue;
|
||||||
|
@ -85,19 +82,13 @@ repeat:
|
||||||
return -ENOTDIR;
|
return -ENOTDIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp,
|
static ssize_t binary_sysctl(const int *name, int nlen,
|
||||||
void __user *newval, size_t newlen)
|
void __user *oldval, size_t __user *oldlenp,
|
||||||
|
void __user *newval, size_t newlen)
|
||||||
|
|
||||||
{
|
{
|
||||||
struct ctl_table_header *head;
|
struct ctl_table_header *head;
|
||||||
int error = -ENOTDIR;
|
ssize_t error = -ENOTDIR;
|
||||||
|
|
||||||
if (nlen <= 0 || nlen >= CTL_MAXNAME)
|
|
||||||
return -ENOTDIR;
|
|
||||||
if (oldval) {
|
|
||||||
int old_len;
|
|
||||||
if (!oldlenp || get_user(old_len, oldlenp))
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (head = sysctl_head_next(NULL); head;
|
for (head = sysctl_head_next(NULL); head;
|
||||||
head = sysctl_head_next(head)) {
|
head = sysctl_head_next(head)) {
|
||||||
|
@ -112,74 +103,76 @@ int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *ol
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args)
|
|
||||||
{
|
|
||||||
struct __sysctl_args tmp;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
if (copy_from_user(&tmp, args, sizeof(tmp)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
error = deprecated_sysctl_warning(&tmp);
|
|
||||||
if (error)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
lock_kernel();
|
|
||||||
error = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,
|
|
||||||
tmp.newval, tmp.newlen);
|
|
||||||
unlock_kernel();
|
|
||||||
out:
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* CONFIG_SYSCTL_SYSCALL */
|
#else /* CONFIG_SYSCTL_SYSCALL */
|
||||||
|
|
||||||
SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args)
|
static ssize_t binary_sysctl(const int *ctl_name, int nlen,
|
||||||
|
void __user *oldval, size_t __user *oldlenp,
|
||||||
|
void __user *newval, size_t newlen)
|
||||||
{
|
{
|
||||||
struct __sysctl_args tmp;
|
return -ENOSYS;
|
||||||
int error;
|
|
||||||
|
|
||||||
if (copy_from_user(&tmp, args, sizeof(tmp)))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
error = deprecated_sysctl_warning(&tmp);
|
|
||||||
|
|
||||||
/* If no error reading the parameters then just -ENOSYS ... */
|
|
||||||
if (!error)
|
|
||||||
error = -ENOSYS;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_SYSCTL_SYSCALL */
|
#endif /* CONFIG_SYSCTL_SYSCALL */
|
||||||
|
|
||||||
static int deprecated_sysctl_warning(struct __sysctl_args *args)
|
static void deprecated_sysctl_warning(const int *name, int nlen)
|
||||||
{
|
{
|
||||||
static int msg_count;
|
static int msg_count;
|
||||||
int name[CTL_MAXNAME];
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Check args->nlen. */
|
|
||||||
if (args->nlen < 0 || args->nlen > CTL_MAXNAME)
|
|
||||||
return -ENOTDIR;
|
|
||||||
|
|
||||||
/* Read in the sysctl name for better debug message logging */
|
|
||||||
for (i = 0; i < args->nlen; i++)
|
|
||||||
if (get_user(name[i], args->name + i))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
/* Ignore accesses to kernel.version */
|
/* Ignore accesses to kernel.version */
|
||||||
if ((args->nlen == 2) && (name[0] == CTL_KERN) && (name[1] == KERN_VERSION))
|
if ((nlen == 2) && (name[0] == CTL_KERN) && (name[1] == KERN_VERSION))
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
if (msg_count < 5) {
|
if (msg_count < 5) {
|
||||||
msg_count++;
|
msg_count++;
|
||||||
printk(KERN_INFO
|
printk(KERN_INFO
|
||||||
"warning: process `%s' used the deprecated sysctl "
|
"warning: process `%s' used the deprecated sysctl "
|
||||||
"system call with ", current->comm);
|
"system call with ", current->comm);
|
||||||
for (i = 0; i < args->nlen; i++)
|
for (i = 0; i < nlen; i++)
|
||||||
printk("%d.", name[i]);
|
printk("%d.", name[i]);
|
||||||
printk("\n");
|
printk("\n");
|
||||||
}
|
}
|
||||||
return 0;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_sysctl(int __user *args_name, int nlen,
|
||||||
|
void __user *oldval, size_t __user *oldlenp,
|
||||||
|
void __user *newval, size_t newlen)
|
||||||
|
{
|
||||||
|
int name[CTL_MAXNAME];
|
||||||
|
size_t oldlen = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (nlen <= 0 || nlen >= CTL_MAXNAME)
|
||||||
|
return -ENOTDIR;
|
||||||
|
if (oldval && !oldlenp)
|
||||||
|
return -EFAULT;
|
||||||
|
if (oldlenp && get_user(oldlen, oldlenp))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
/* Read in the sysctl name for simplicity */
|
||||||
|
for (i = 0; i < nlen; i++)
|
||||||
|
if (get_user(name[i], args_name + i))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
deprecated_sysctl_warning(name, nlen);
|
||||||
|
|
||||||
|
return binary_sysctl(name, nlen, oldval, oldlenp, newval, newlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args)
|
||||||
|
{
|
||||||
|
struct __sysctl_args tmp;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (copy_from_user(&tmp, args, sizeof(tmp)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
lock_kernel();
|
||||||
|
error = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,
|
||||||
|
tmp.newval, tmp.newlen);
|
||||||
|
unlock_kernel();
|
||||||
|
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue