Skip to content

Commit

Permalink
sysctl: Refactor the binary sysctl handling to remove duplicate code
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Eric W. Biederman committed Nov 6, 2009
1 parent afa588b commit 2830b68
Showing 1 changed file with 58 additions and 65 deletions.
123 changes: 58 additions & 65 deletions kernel/sysctl_binary.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
#include <linux/ctype.h>
#include <linux/smp_lock.h>

static int deprecated_sysctl_warning(struct __sysctl_args *args);

#ifdef CONFIG_SYSCTL_SYSCALL

/* Perform the actual read/write of a sysctl table entry. */
Expand Down Expand Up @@ -51,7 +49,7 @@ static int do_sysctl_strategy(struct ctl_table_root *root,
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 *newval, size_t newlen,
struct ctl_table_root *root,
Expand All @@ -61,8 +59,7 @@ static int parse_table(int __user *name, int nlen,
repeat:
if (!nlen)
return -ENOTDIR;
if (get_user(n, name))
return -EFAULT;
n = *name;
for ( ; table->ctl_name || table->procname; table++) {
if (!table->ctl_name)
continue;
Expand All @@ -85,19 +82,13 @@ static int parse_table(int __user *name, int nlen,
return -ENOTDIR;
}

int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp,
void __user *newval, size_t newlen)
static ssize_t binary_sysctl(const int *name, int nlen,
void __user *oldval, size_t __user *oldlenp,
void __user *newval, size_t newlen)

{
struct ctl_table_header *head;
int error = -ENOTDIR;

if (nlen <= 0 || nlen >= CTL_MAXNAME)
return -ENOTDIR;
if (oldval) {
int old_len;
if (!oldlenp || get_user(old_len, oldlenp))
return -EFAULT;
}
ssize_t error = -ENOTDIR;

for (head = sysctl_head_next(NULL); head;
head = sysctl_head_next(head)) {
Expand All @@ -112,74 +103,76 @@ int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *ol
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 */

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;
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;
return -ENOSYS;
}

#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;
int name[CTL_MAXNAME];
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 */
if ((args->nlen == 2) && (name[0] == CTL_KERN) && (name[1] == KERN_VERSION))
return 0;
if ((nlen == 2) && (name[0] == CTL_KERN) && (name[1] == KERN_VERSION))
return;

if (msg_count < 5) {
msg_count++;
printk(KERN_INFO
"warning: process `%s' used the deprecated sysctl "
"system call with ", current->comm);
for (i = 0; i < args->nlen; i++)
for (i = 0; i < nlen; i++)
printk("%d.", name[i]);
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;
}

0 comments on commit 2830b68

Please sign in to comment.