From 2830b68361a9f58354ad043c6d85043ea917f907 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 3 Apr 2009 00:09:33 -0700 Subject: 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 --- kernel/sysctl_binary.c | 123 +++++++++++++++++++++++-------------------------- 1 file changed, 58 insertions(+), 65 deletions(-) (limited to 'kernel/sysctl_binary.c') diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index eceeed20ca8..930a31cd708 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -14,8 +14,6 @@ #include #include -static int deprecated_sysctl_warning(struct __sysctl_args *args); - #ifdef CONFIG_SYSCTL_SYSCALL /* 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; } -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, @@ -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; @@ -85,19 +82,13 @@ repeat: 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)) { @@ -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; } -- cgit v1.2.3