Commit 2830b68361a9f58354ad043c6d85043ea917f907
1 parent
afa588b265
Exists in
master
and in
39 other branches
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>
Showing 1 changed file with 58 additions and 65 deletions Side-by-side Diff
kernel/sysctl_binary.c
... | ... | @@ -14,8 +14,6 @@ |
14 | 14 | #include <linux/ctype.h> |
15 | 15 | #include <linux/smp_lock.h> |
16 | 16 | |
17 | -static int deprecated_sysctl_warning(struct __sysctl_args *args); | |
18 | - | |
19 | 17 | #ifdef CONFIG_SYSCTL_SYSCALL |
20 | 18 | |
21 | 19 | /* Perform the actual read/write of a sysctl table entry. */ |
... | ... | @@ -51,7 +49,7 @@ |
51 | 49 | return 0; |
52 | 50 | } |
53 | 51 | |
54 | -static int parse_table(int __user *name, int nlen, | |
52 | +static int parse_table(const int *name, int nlen, | |
55 | 53 | void __user *oldval, size_t __user *oldlenp, |
56 | 54 | void __user *newval, size_t newlen, |
57 | 55 | struct ctl_table_root *root, |
... | ... | @@ -61,8 +59,7 @@ |
61 | 59 | repeat: |
62 | 60 | if (!nlen) |
63 | 61 | return -ENOTDIR; |
64 | - if (get_user(n, name)) | |
65 | - return -EFAULT; | |
62 | + n = *name; | |
66 | 63 | for ( ; table->ctl_name || table->procname; table++) { |
67 | 64 | if (!table->ctl_name) |
68 | 65 | continue; |
69 | 66 | |
70 | 67 | |
... | ... | @@ -85,20 +82,14 @@ |
85 | 82 | return -ENOTDIR; |
86 | 83 | } |
87 | 84 | |
88 | -int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, | |
89 | - void __user *newval, size_t newlen) | |
85 | +static ssize_t binary_sysctl(const int *name, int nlen, | |
86 | + void __user *oldval, size_t __user *oldlenp, | |
87 | + void __user *newval, size_t newlen) | |
88 | + | |
90 | 89 | { |
91 | 90 | struct ctl_table_header *head; |
92 | - int error = -ENOTDIR; | |
91 | + ssize_t error = -ENOTDIR; | |
93 | 92 | |
94 | - if (nlen <= 0 || nlen >= CTL_MAXNAME) | |
95 | - return -ENOTDIR; | |
96 | - if (oldval) { | |
97 | - int old_len; | |
98 | - if (!oldlenp || get_user(old_len, oldlenp)) | |
99 | - return -EFAULT; | |
100 | - } | |
101 | - | |
102 | 93 | for (head = sysctl_head_next(NULL); head; |
103 | 94 | head = sysctl_head_next(head)) { |
104 | 95 | error = parse_table(name, nlen, oldval, oldlenp, |
105 | 96 | |
106 | 97 | |
107 | 98 | |
108 | 99 | |
109 | 100 | |
110 | 101 | |
111 | 102 | |
112 | 103 | |
... | ... | @@ -112,75 +103,77 @@ |
112 | 103 | return error; |
113 | 104 | } |
114 | 105 | |
115 | -SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args) | |
116 | -{ | |
117 | - struct __sysctl_args tmp; | |
118 | - int error; | |
119 | - | |
120 | - if (copy_from_user(&tmp, args, sizeof(tmp))) | |
121 | - return -EFAULT; | |
122 | - | |
123 | - error = deprecated_sysctl_warning(&tmp); | |
124 | - if (error) | |
125 | - goto out; | |
126 | - | |
127 | - lock_kernel(); | |
128 | - error = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp, | |
129 | - tmp.newval, tmp.newlen); | |
130 | - unlock_kernel(); | |
131 | -out: | |
132 | - return error; | |
133 | -} | |
134 | - | |
135 | 106 | #else /* CONFIG_SYSCTL_SYSCALL */ |
136 | 107 | |
137 | -SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args) | |
108 | +static ssize_t binary_sysctl(const int *ctl_name, int nlen, | |
109 | + void __user *oldval, size_t __user *oldlenp, | |
110 | + void __user *newval, size_t newlen) | |
138 | 111 | { |
139 | - struct __sysctl_args tmp; | |
140 | - int error; | |
141 | - | |
142 | - if (copy_from_user(&tmp, args, sizeof(tmp))) | |
143 | - return -EFAULT; | |
144 | - | |
145 | - error = deprecated_sysctl_warning(&tmp); | |
146 | - | |
147 | - /* If no error reading the parameters then just -ENOSYS ... */ | |
148 | - if (!error) | |
149 | - error = -ENOSYS; | |
150 | - | |
151 | - return error; | |
112 | + return -ENOSYS; | |
152 | 113 | } |
153 | 114 | |
154 | 115 | #endif /* CONFIG_SYSCTL_SYSCALL */ |
155 | 116 | |
156 | -static int deprecated_sysctl_warning(struct __sysctl_args *args) | |
117 | +static void deprecated_sysctl_warning(const int *name, int nlen) | |
157 | 118 | { |
158 | 119 | static int msg_count; |
159 | - int name[CTL_MAXNAME]; | |
160 | 120 | int i; |
161 | 121 | |
162 | - /* Check args->nlen. */ | |
163 | - if (args->nlen < 0 || args->nlen > CTL_MAXNAME) | |
164 | - return -ENOTDIR; | |
165 | - | |
166 | - /* Read in the sysctl name for better debug message logging */ | |
167 | - for (i = 0; i < args->nlen; i++) | |
168 | - if (get_user(name[i], args->name + i)) | |
169 | - return -EFAULT; | |
170 | - | |
171 | 122 | /* Ignore accesses to kernel.version */ |
172 | - if ((args->nlen == 2) && (name[0] == CTL_KERN) && (name[1] == KERN_VERSION)) | |
173 | - return 0; | |
123 | + if ((nlen == 2) && (name[0] == CTL_KERN) && (name[1] == KERN_VERSION)) | |
124 | + return; | |
174 | 125 | |
175 | 126 | if (msg_count < 5) { |
176 | 127 | msg_count++; |
177 | 128 | printk(KERN_INFO |
178 | 129 | "warning: process `%s' used the deprecated sysctl " |
179 | 130 | "system call with ", current->comm); |
180 | - for (i = 0; i < args->nlen; i++) | |
131 | + for (i = 0; i < nlen; i++) | |
181 | 132 | printk("%d.", name[i]); |
182 | 133 | printk("\n"); |
183 | 134 | } |
184 | - return 0; | |
135 | + return; | |
136 | +} | |
137 | + | |
138 | +int do_sysctl(int __user *args_name, int nlen, | |
139 | + void __user *oldval, size_t __user *oldlenp, | |
140 | + void __user *newval, size_t newlen) | |
141 | +{ | |
142 | + int name[CTL_MAXNAME]; | |
143 | + size_t oldlen = 0; | |
144 | + int i; | |
145 | + | |
146 | + if (nlen <= 0 || nlen >= CTL_MAXNAME) | |
147 | + return -ENOTDIR; | |
148 | + if (oldval && !oldlenp) | |
149 | + return -EFAULT; | |
150 | + if (oldlenp && get_user(oldlen, oldlenp)) | |
151 | + return -EFAULT; | |
152 | + | |
153 | + /* Read in the sysctl name for simplicity */ | |
154 | + for (i = 0; i < nlen; i++) | |
155 | + if (get_user(name[i], args_name + i)) | |
156 | + return -EFAULT; | |
157 | + | |
158 | + deprecated_sysctl_warning(name, nlen); | |
159 | + | |
160 | + return binary_sysctl(name, nlen, oldval, oldlenp, newval, newlen); | |
161 | +} | |
162 | + | |
163 | + | |
164 | +SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args) | |
165 | +{ | |
166 | + struct __sysctl_args tmp; | |
167 | + int error; | |
168 | + | |
169 | + if (copy_from_user(&tmp, args, sizeof(tmp))) | |
170 | + return -EFAULT; | |
171 | + | |
172 | + lock_kernel(); | |
173 | + error = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp, | |
174 | + tmp.newval, tmp.newlen); | |
175 | + unlock_kernel(); | |
176 | + | |
177 | + return error; | |
185 | 178 | } |