aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/sysctl.c
diff options
context:
space:
mode:
authorAndi Kleen <andi@firstfloor.org>2008-10-16 01:01:41 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-10-16 14:21:31 -0400
commit25ddbb18aae33ad255eb9f35aacebe3af01e1e9c (patch)
tree8df1f840a226ed640c2096710b7d0f1f4d1b88aa /kernel/sysctl.c
parent889d51a10712b6fd6175196626de2116858394f4 (diff)
Make the taint flags reliable
It's somewhat unlikely that it happens, but right now a race window between interrupts or machine checks or oopses could corrupt the tainted bitmap because it is modified in a non atomic fashion. Convert the taint variable to an unsigned long and use only atomic bit operations on it. Unfortunately this means the intvec sysctl functions cannot be used on it anymore. It turned out the taint sysctl handler could actually be simplified a bit (since it only increases capabilities) so this patch actually removes code. [akpm@linux-foundation.org: remove unneeded include] Signed-off-by: Andi Kleen <ak@linux.intel.com> Cc: Ingo Molnar <mingo@elte.hu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/sysctl.c')
-rw-r--r--kernel/sysctl.c67
1 files changed, 28 insertions, 39 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index cfc5295f1e82..ec88fcc9a0d2 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -149,7 +149,7 @@ extern int max_lock_depth;
149#ifdef CONFIG_PROC_SYSCTL 149#ifdef CONFIG_PROC_SYSCTL
150static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp, 150static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp,
151 void __user *buffer, size_t *lenp, loff_t *ppos); 151 void __user *buffer, size_t *lenp, loff_t *ppos);
152static int proc_dointvec_taint(struct ctl_table *table, int write, struct file *filp, 152static int proc_taint(struct ctl_table *table, int write, struct file *filp,
153 void __user *buffer, size_t *lenp, loff_t *ppos); 153 void __user *buffer, size_t *lenp, loff_t *ppos);
154#endif 154#endif
155 155
@@ -379,10 +379,9 @@ static struct ctl_table kern_table[] = {
379#ifdef CONFIG_PROC_SYSCTL 379#ifdef CONFIG_PROC_SYSCTL
380 { 380 {
381 .procname = "tainted", 381 .procname = "tainted",
382 .data = &tainted, 382 .maxlen = sizeof(long),
383 .maxlen = sizeof(int),
384 .mode = 0644, 383 .mode = 0644,
385 .proc_handler = &proc_dointvec_taint, 384 .proc_handler = &proc_taint,
386 }, 385 },
387#endif 386#endif
388#ifdef CONFIG_LATENCYTOP 387#ifdef CONFIG_LATENCYTOP
@@ -2228,49 +2227,39 @@ int proc_dointvec(struct ctl_table *table, int write, struct file *filp,
2228 NULL,NULL); 2227 NULL,NULL);
2229} 2228}
2230 2229
2231#define OP_SET 0
2232#define OP_AND 1
2233#define OP_OR 2
2234
2235static int do_proc_dointvec_bset_conv(int *negp, unsigned long *lvalp,
2236 int *valp,
2237 int write, void *data)
2238{
2239 int op = *(int *)data;
2240 if (write) {
2241 int val = *negp ? -*lvalp : *lvalp;
2242 switch(op) {
2243 case OP_SET: *valp = val; break;
2244 case OP_AND: *valp &= val; break;
2245 case OP_OR: *valp |= val; break;
2246 }
2247 } else {
2248 int val = *valp;
2249 if (val < 0) {
2250 *negp = -1;
2251 *lvalp = (unsigned long)-val;
2252 } else {
2253 *negp = 0;
2254 *lvalp = (unsigned long)val;
2255 }
2256 }
2257 return 0;
2258}
2259
2260/* 2230/*
2261 * Taint values can only be increased 2231 * Taint values can only be increased
2232 * This means we can safely use a temporary.
2262 */ 2233 */
2263static int proc_dointvec_taint(struct ctl_table *table, int write, struct file *filp, 2234static int proc_taint(struct ctl_table *table, int write, struct file *filp,
2264 void __user *buffer, size_t *lenp, loff_t *ppos) 2235 void __user *buffer, size_t *lenp, loff_t *ppos)
2265{ 2236{
2266 int op; 2237 struct ctl_table t;
2238 unsigned long tmptaint = get_taint();
2239 int err;
2267 2240
2268 if (write && !capable(CAP_SYS_ADMIN)) 2241 if (write && !capable(CAP_SYS_ADMIN))
2269 return -EPERM; 2242 return -EPERM;
2270 2243
2271 op = OP_OR; 2244 t = *table;
2272 return do_proc_dointvec(table,write,filp,buffer,lenp,ppos, 2245 t.data = &tmptaint;
2273 do_proc_dointvec_bset_conv,&op); 2246 err = proc_doulongvec_minmax(&t, write, filp, buffer, lenp, ppos);
2247 if (err < 0)
2248 return err;
2249
2250 if (write) {
2251 /*
2252 * Poor man's atomic or. Not worth adding a primitive
2253 * to everyone's atomic.h for this
2254 */
2255 int i;
2256 for (i = 0; i < BITS_PER_LONG && tmptaint >> i; i++) {
2257 if ((tmptaint >> i) & 1)
2258 add_taint(i);
2259 }
2260 }
2261
2262 return err;
2274} 2263}
2275 2264
2276struct do_proc_dointvec_minmax_conv_param { 2265struct do_proc_dointvec_minmax_conv_param {