aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/sysctl.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2005-11-04 05:18:40 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2005-11-08 20:57:30 -0500
commit330d57fb98a916fa8e1363846540dd420e99499a (patch)
tree841d5e5eeda46fd95ac03c36964919818a9bc3a6 /kernel/sysctl.c
parent8546df6f357dadf1989ad8da9309c9524fd56cdf (diff)
[PATCH] Fix sysctl unregistration oops (CVE-2005-2709)
You could open the /proc/sys/net/ipv4/conf/<if>/<whatever> file, then wait for interface to go away, try to grab as much memory as possible in hope to hit the (kfreed) ctl_table. Then fill it with pointers to your function. Then do read from file you've opened and if you are lucky, you'll get it called as ->proc_handler() in kernel mode. So this is at least an Oops and possibly more. It does depend on an interface going away though, so less of a security risk than it would otherwise be. Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel/sysctl.c')
-rw-r--r--kernel/sysctl.c136
1 files changed, 107 insertions, 29 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index c4f35f96884d..9990e10192e8 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -169,7 +169,7 @@ struct file_operations proc_sys_file_operations = {
169 169
170extern struct proc_dir_entry *proc_sys_root; 170extern struct proc_dir_entry *proc_sys_root;
171 171
172static void register_proc_table(ctl_table *, struct proc_dir_entry *); 172static void register_proc_table(ctl_table *, struct proc_dir_entry *, void *);
173static void unregister_proc_table(ctl_table *, struct proc_dir_entry *); 173static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
174#endif 174#endif
175 175
@@ -992,10 +992,51 @@ static ctl_table dev_table[] = {
992 992
993extern void init_irq_proc (void); 993extern void init_irq_proc (void);
994 994
995static DEFINE_SPINLOCK(sysctl_lock);
996
997/* called under sysctl_lock */
998static int use_table(struct ctl_table_header *p)
999{
1000 if (unlikely(p->unregistering))
1001 return 0;
1002 p->used++;
1003 return 1;
1004}
1005
1006/* called under sysctl_lock */
1007static void unuse_table(struct ctl_table_header *p)
1008{
1009 if (!--p->used)
1010 if (unlikely(p->unregistering))
1011 complete(p->unregistering);
1012}
1013
1014/* called under sysctl_lock, will reacquire if has to wait */
1015static void start_unregistering(struct ctl_table_header *p)
1016{
1017 /*
1018 * if p->used is 0, nobody will ever touch that entry again;
1019 * we'll eliminate all paths to it before dropping sysctl_lock
1020 */
1021 if (unlikely(p->used)) {
1022 struct completion wait;
1023 init_completion(&wait);
1024 p->unregistering = &wait;
1025 spin_unlock(&sysctl_lock);
1026 wait_for_completion(&wait);
1027 spin_lock(&sysctl_lock);
1028 }
1029 /*
1030 * do not remove from the list until nobody holds it; walking the
1031 * list in do_sysctl() relies on that.
1032 */
1033 list_del_init(&p->ctl_entry);
1034}
1035
995void __init sysctl_init(void) 1036void __init sysctl_init(void)
996{ 1037{
997#ifdef CONFIG_PROC_FS 1038#ifdef CONFIG_PROC_FS
998 register_proc_table(root_table, proc_sys_root); 1039 register_proc_table(root_table, proc_sys_root, &root_table_header);
999 init_irq_proc(); 1040 init_irq_proc();
1000#endif 1041#endif
1001} 1042}
@@ -1004,6 +1045,7 @@ int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *ol
1004 void __user *newval, size_t newlen) 1045 void __user *newval, size_t newlen)
1005{ 1046{
1006 struct list_head *tmp; 1047 struct list_head *tmp;
1048 int error = -ENOTDIR;
1007 1049
1008 if (nlen <= 0 || nlen >= CTL_MAXNAME) 1050 if (nlen <= 0 || nlen >= CTL_MAXNAME)
1009 return -ENOTDIR; 1051 return -ENOTDIR;
@@ -1012,20 +1054,30 @@ int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *ol
1012 if (!oldlenp || get_user(old_len, oldlenp)) 1054 if (!oldlenp || get_user(old_len, oldlenp))
1013 return -EFAULT; 1055 return -EFAULT;
1014 } 1056 }
1057 spin_lock(&sysctl_lock);
1015 tmp = &root_table_header.ctl_entry; 1058 tmp = &root_table_header.ctl_entry;
1016 do { 1059 do {
1017 struct ctl_table_header *head = 1060 struct ctl_table_header *head =
1018 list_entry(tmp, struct ctl_table_header, ctl_entry); 1061 list_entry(tmp, struct ctl_table_header, ctl_entry);
1019 void *context = NULL; 1062 void *context = NULL;
1020 int error = parse_table(name, nlen, oldval, oldlenp, 1063
1064 if (!use_table(head))
1065 continue;
1066
1067 spin_unlock(&sysctl_lock);
1068
1069 error = parse_table(name, nlen, oldval, oldlenp,
1021 newval, newlen, head->ctl_table, 1070 newval, newlen, head->ctl_table,
1022 &context); 1071 &context);
1023 kfree(context); 1072 kfree(context);
1073
1074 spin_lock(&sysctl_lock);
1075 unuse_table(head);
1024 if (error != -ENOTDIR) 1076 if (error != -ENOTDIR)
1025 return error; 1077 break;
1026 tmp = tmp->next; 1078 } while ((tmp = tmp->next) != &root_table_header.ctl_entry);
1027 } while (tmp != &root_table_header.ctl_entry); 1079 spin_unlock(&sysctl_lock);
1028 return -ENOTDIR; 1080 return error;
1029} 1081}
1030 1082
1031asmlinkage long sys_sysctl(struct __sysctl_args __user *args) 1083asmlinkage long sys_sysctl(struct __sysctl_args __user *args)
@@ -1236,12 +1288,16 @@ struct ctl_table_header *register_sysctl_table(ctl_table * table,
1236 return NULL; 1288 return NULL;
1237 tmp->ctl_table = table; 1289 tmp->ctl_table = table;
1238 INIT_LIST_HEAD(&tmp->ctl_entry); 1290 INIT_LIST_HEAD(&tmp->ctl_entry);
1291 tmp->used = 0;
1292 tmp->unregistering = NULL;
1293 spin_lock(&sysctl_lock);
1239 if (insert_at_head) 1294 if (insert_at_head)
1240 list_add(&tmp->ctl_entry, &root_table_header.ctl_entry); 1295 list_add(&tmp->ctl_entry, &root_table_header.ctl_entry);
1241 else 1296 else
1242 list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry); 1297 list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry);
1298 spin_unlock(&sysctl_lock);
1243#ifdef CONFIG_PROC_FS 1299#ifdef CONFIG_PROC_FS
1244 register_proc_table(table, proc_sys_root); 1300 register_proc_table(table, proc_sys_root, tmp);
1245#endif 1301#endif
1246 return tmp; 1302 return tmp;
1247} 1303}
@@ -1255,10 +1311,13 @@ struct ctl_table_header *register_sysctl_table(ctl_table * table,
1255 */ 1311 */
1256void unregister_sysctl_table(struct ctl_table_header * header) 1312void unregister_sysctl_table(struct ctl_table_header * header)
1257{ 1313{
1258 list_del(&header->ctl_entry); 1314 might_sleep();
1315 spin_lock(&sysctl_lock);
1316 start_unregistering(header);
1259#ifdef CONFIG_PROC_FS 1317#ifdef CONFIG_PROC_FS
1260 unregister_proc_table(header->ctl_table, proc_sys_root); 1318 unregister_proc_table(header->ctl_table, proc_sys_root);
1261#endif 1319#endif
1320 spin_unlock(&sysctl_lock);
1262 kfree(header); 1321 kfree(header);
1263} 1322}
1264 1323
@@ -1269,7 +1328,7 @@ void unregister_sysctl_table(struct ctl_table_header * header)
1269#ifdef CONFIG_PROC_FS 1328#ifdef CONFIG_PROC_FS
1270 1329
1271/* Scan the sysctl entries in table and add them all into /proc */ 1330/* Scan the sysctl entries in table and add them all into /proc */
1272static void register_proc_table(ctl_table * table, struct proc_dir_entry *root) 1331static void register_proc_table(ctl_table * table, struct proc_dir_entry *root, void *set)
1273{ 1332{
1274 struct proc_dir_entry *de; 1333 struct proc_dir_entry *de;
1275 int len; 1334 int len;
@@ -1305,13 +1364,14 @@ static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
1305 de = create_proc_entry(table->procname, mode, root); 1364 de = create_proc_entry(table->procname, mode, root);
1306 if (!de) 1365 if (!de)
1307 continue; 1366 continue;
1367 de->set = set;
1308 de->data = (void *) table; 1368 de->data = (void *) table;
1309 if (table->proc_handler) 1369 if (table->proc_handler)
1310 de->proc_fops = &proc_sys_file_operations; 1370 de->proc_fops = &proc_sys_file_operations;
1311 } 1371 }
1312 table->de = de; 1372 table->de = de;
1313 if (de->mode & S_IFDIR) 1373 if (de->mode & S_IFDIR)
1314 register_proc_table(table->child, de); 1374 register_proc_table(table->child, de, set);
1315 } 1375 }
1316} 1376}
1317 1377
@@ -1336,6 +1396,13 @@ static void unregister_proc_table(ctl_table * table, struct proc_dir_entry *root
1336 continue; 1396 continue;
1337 } 1397 }
1338 1398
1399 /*
1400 * In any case, mark the entry as goner; we'll keep it
1401 * around if it's busy, but we'll know to do nothing with
1402 * its fields. We are under sysctl_lock here.
1403 */
1404 de->data = NULL;
1405
1339 /* Don't unregister proc entries that are still being used.. */ 1406 /* Don't unregister proc entries that are still being used.. */
1340 if (atomic_read(&de->count)) 1407 if (atomic_read(&de->count))
1341 continue; 1408 continue;
@@ -1349,27 +1416,38 @@ static ssize_t do_rw_proc(int write, struct file * file, char __user * buf,
1349 size_t count, loff_t *ppos) 1416 size_t count, loff_t *ppos)
1350{ 1417{
1351 int op; 1418 int op;
1352 struct proc_dir_entry *de; 1419 struct proc_dir_entry *de = PDE(file->f_dentry->d_inode);
1353 struct ctl_table *table; 1420 struct ctl_table *table;
1354 size_t res; 1421 size_t res;
1355 ssize_t error; 1422 ssize_t error = -ENOTDIR;
1356
1357 de = PDE(file->f_dentry->d_inode);
1358 if (!de || !de->data)
1359 return -ENOTDIR;
1360 table = (struct ctl_table *) de->data;
1361 if (!table || !table->proc_handler)
1362 return -ENOTDIR;
1363 op = (write ? 002 : 004);
1364 if (ctl_perm(table, op))
1365 return -EPERM;
1366 1423
1367 res = count; 1424 spin_lock(&sysctl_lock);
1368 1425 if (de && de->data && use_table(de->set)) {
1369 error = (*table->proc_handler) (table, write, file, buf, &res, ppos); 1426 /*
1370 if (error) 1427 * at that point we know that sysctl was not unregistered
1371 return error; 1428 * and won't be until we finish
1372 return res; 1429 */
1430 spin_unlock(&sysctl_lock);
1431 table = (struct ctl_table *) de->data;
1432 if (!table || !table->proc_handler)
1433 goto out;
1434 error = -EPERM;
1435 op = (write ? 002 : 004);
1436 if (ctl_perm(table, op))
1437 goto out;
1438
1439 /* careful: calling conventions are nasty here */
1440 res = count;
1441 error = (*table->proc_handler)(table, write, file,
1442 buf, &res, ppos);
1443 if (!error)
1444 error = res;
1445 out:
1446 spin_lock(&sysctl_lock);
1447 unuse_table(de->set);
1448 }
1449 spin_unlock(&sysctl_lock);
1450 return error;
1373} 1451}
1374 1452
1375static int proc_opensys(struct inode *inode, struct file *file) 1453static int proc_opensys(struct inode *inode, struct file *file)