diff options
| -rw-r--r-- | include/linux/sysctl.h | 17 | ||||
| -rw-r--r-- | kernel/sysctl.c | 93 | ||||
| -rw-r--r-- | kernel/sysctl_check.c | 25 |
3 files changed, 112 insertions, 23 deletions
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 77de3bfd8744..89faebfe48b8 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h | |||
| @@ -945,7 +945,10 @@ enum | |||
| 945 | 945 | ||
| 946 | /* For the /proc/sys support */ | 946 | /* For the /proc/sys support */ |
| 947 | struct ctl_table; | 947 | struct ctl_table; |
| 948 | struct nsproxy; | ||
| 948 | extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); | 949 | extern struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev); |
| 950 | extern struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, | ||
| 951 | struct ctl_table_header *prev); | ||
| 949 | extern void sysctl_head_finish(struct ctl_table_header *prev); | 952 | extern void sysctl_head_finish(struct ctl_table_header *prev); |
| 950 | extern int sysctl_perm(struct ctl_table *table, int op); | 953 | extern int sysctl_perm(struct ctl_table *table, int op); |
| 951 | 954 | ||
| @@ -1049,6 +1052,13 @@ struct ctl_table | |||
| 1049 | void *extra2; | 1052 | void *extra2; |
| 1050 | }; | 1053 | }; |
| 1051 | 1054 | ||
| 1055 | struct ctl_table_root { | ||
| 1056 | struct list_head root_list; | ||
| 1057 | struct list_head header_list; | ||
| 1058 | struct list_head *(*lookup)(struct ctl_table_root *root, | ||
| 1059 | struct nsproxy *namespaces); | ||
| 1060 | }; | ||
| 1061 | |||
| 1052 | /* struct ctl_table_header is used to maintain dynamic lists of | 1062 | /* struct ctl_table_header is used to maintain dynamic lists of |
| 1053 | struct ctl_table trees. */ | 1063 | struct ctl_table trees. */ |
| 1054 | struct ctl_table_header | 1064 | struct ctl_table_header |
| @@ -1058,6 +1068,7 @@ struct ctl_table_header | |||
| 1058 | int used; | 1068 | int used; |
| 1059 | struct completion *unregistering; | 1069 | struct completion *unregistering; |
| 1060 | struct ctl_table *ctl_table_arg; | 1070 | struct ctl_table *ctl_table_arg; |
| 1071 | struct ctl_table_root *root; | ||
| 1061 | }; | 1072 | }; |
| 1062 | 1073 | ||
| 1063 | /* struct ctl_path describes where in the hierarchy a table is added */ | 1074 | /* struct ctl_path describes where in the hierarchy a table is added */ |
| @@ -1066,12 +1077,16 @@ struct ctl_path { | |||
| 1066 | int ctl_name; | 1077 | int ctl_name; |
| 1067 | }; | 1078 | }; |
| 1068 | 1079 | ||
| 1080 | void register_sysctl_root(struct ctl_table_root *root); | ||
| 1081 | struct ctl_table_header *__register_sysctl_paths( | ||
| 1082 | struct ctl_table_root *root, struct nsproxy *namespaces, | ||
| 1083 | const struct ctl_path *path, struct ctl_table *table); | ||
| 1069 | struct ctl_table_header *register_sysctl_table(struct ctl_table * table); | 1084 | struct ctl_table_header *register_sysctl_table(struct ctl_table * table); |
| 1070 | struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, | 1085 | struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, |
| 1071 | struct ctl_table *table); | 1086 | struct ctl_table *table); |
| 1072 | 1087 | ||
| 1073 | void unregister_sysctl_table(struct ctl_table_header * table); | 1088 | void unregister_sysctl_table(struct ctl_table_header * table); |
| 1074 | int sysctl_check_table(struct ctl_table *table); | 1089 | int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table); |
| 1075 | 1090 | ||
| 1076 | #else /* __KERNEL__ */ | 1091 | #else /* __KERNEL__ */ |
| 1077 | 1092 | ||
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 89b7d95ecf51..45e76f209dcb 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -157,8 +157,16 @@ static int proc_dointvec_taint(struct ctl_table *table, int write, struct file * | |||
| 157 | #endif | 157 | #endif |
| 158 | 158 | ||
| 159 | static struct ctl_table root_table[]; | 159 | static struct ctl_table root_table[]; |
| 160 | static struct ctl_table_header root_table_header = | 160 | static struct ctl_table_root sysctl_table_root; |
| 161 | { root_table, LIST_HEAD_INIT(root_table_header.ctl_entry) }; | 161 | static struct ctl_table_header root_table_header = { |
| 162 | .ctl_table = root_table, | ||
| 163 | .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.header_list), | ||
| 164 | .root = &sysctl_table_root, | ||
| 165 | }; | ||
| 166 | static struct ctl_table_root sysctl_table_root = { | ||
| 167 | .root_list = LIST_HEAD_INIT(sysctl_table_root.root_list), | ||
| 168 | .header_list = LIST_HEAD_INIT(root_table_header.ctl_entry), | ||
| 169 | }; | ||
| 162 | 170 | ||
| 163 | static struct ctl_table kern_table[]; | 171 | static struct ctl_table kern_table[]; |
| 164 | static struct ctl_table vm_table[]; | 172 | static struct ctl_table vm_table[]; |
| @@ -1371,12 +1379,27 @@ void sysctl_head_finish(struct ctl_table_header *head) | |||
| 1371 | spin_unlock(&sysctl_lock); | 1379 | spin_unlock(&sysctl_lock); |
| 1372 | } | 1380 | } |
| 1373 | 1381 | ||
| 1374 | struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev) | 1382 | static struct list_head * |
| 1383 | lookup_header_list(struct ctl_table_root *root, struct nsproxy *namespaces) | ||
| 1375 | { | 1384 | { |
| 1385 | struct list_head *header_list; | ||
| 1386 | header_list = &root->header_list; | ||
| 1387 | if (root->lookup) | ||
| 1388 | header_list = root->lookup(root, namespaces); | ||
| 1389 | return header_list; | ||
| 1390 | } | ||
| 1391 | |||
| 1392 | struct ctl_table_header *__sysctl_head_next(struct nsproxy *namespaces, | ||
| 1393 | struct ctl_table_header *prev) | ||
| 1394 | { | ||
| 1395 | struct ctl_table_root *root; | ||
| 1396 | struct list_head *header_list; | ||
| 1376 | struct ctl_table_header *head; | 1397 | struct ctl_table_header *head; |
| 1377 | struct list_head *tmp; | 1398 | struct list_head *tmp; |
| 1399 | |||
| 1378 | spin_lock(&sysctl_lock); | 1400 | spin_lock(&sysctl_lock); |
| 1379 | if (prev) { | 1401 | if (prev) { |
| 1402 | head = prev; | ||
| 1380 | tmp = &prev->ctl_entry; | 1403 | tmp = &prev->ctl_entry; |
| 1381 | unuse_table(prev); | 1404 | unuse_table(prev); |
| 1382 | goto next; | 1405 | goto next; |
| @@ -1390,14 +1413,38 @@ struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev) | |||
| 1390 | spin_unlock(&sysctl_lock); | 1413 | spin_unlock(&sysctl_lock); |
| 1391 | return head; | 1414 | return head; |
| 1392 | next: | 1415 | next: |
| 1416 | root = head->root; | ||
| 1393 | tmp = tmp->next; | 1417 | tmp = tmp->next; |
| 1394 | if (tmp == &root_table_header.ctl_entry) | 1418 | header_list = lookup_header_list(root, namespaces); |
| 1395 | break; | 1419 | if (tmp != header_list) |
| 1420 | continue; | ||
| 1421 | |||
| 1422 | do { | ||
| 1423 | root = list_entry(root->root_list.next, | ||
| 1424 | struct ctl_table_root, root_list); | ||
| 1425 | if (root == &sysctl_table_root) | ||
| 1426 | goto out; | ||
| 1427 | header_list = lookup_header_list(root, namespaces); | ||
| 1428 | } while (list_empty(header_list)); | ||
| 1429 | tmp = header_list->next; | ||
| 1396 | } | 1430 | } |
| 1431 | out: | ||
| 1397 | spin_unlock(&sysctl_lock); | 1432 | spin_unlock(&sysctl_lock); |
| 1398 | return NULL; | 1433 | return NULL; |
| 1399 | } | 1434 | } |
| 1400 | 1435 | ||
| 1436 | struct ctl_table_header *sysctl_head_next(struct ctl_table_header *prev) | ||
| 1437 | { | ||
| 1438 | return __sysctl_head_next(current->nsproxy, prev); | ||
| 1439 | } | ||
| 1440 | |||
| 1441 | void register_sysctl_root(struct ctl_table_root *root) | ||
| 1442 | { | ||
| 1443 | spin_lock(&sysctl_lock); | ||
| 1444 | list_add_tail(&root->root_list, &sysctl_table_root.root_list); | ||
| 1445 | spin_unlock(&sysctl_lock); | ||
| 1446 | } | ||
| 1447 | |||
| 1401 | #ifdef CONFIG_SYSCTL_SYSCALL | 1448 | #ifdef CONFIG_SYSCTL_SYSCALL |
| 1402 | int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, | 1449 | int do_sysctl(int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, |
| 1403 | void __user *newval, size_t newlen) | 1450 | void __user *newval, size_t newlen) |
| @@ -1554,14 +1601,16 @@ static __init int sysctl_init(void) | |||
| 1554 | { | 1601 | { |
| 1555 | int err; | 1602 | int err; |
| 1556 | sysctl_set_parent(NULL, root_table); | 1603 | sysctl_set_parent(NULL, root_table); |
| 1557 | err = sysctl_check_table(root_table); | 1604 | err = sysctl_check_table(current->nsproxy, root_table); |
| 1558 | return 0; | 1605 | return 0; |
| 1559 | } | 1606 | } |
| 1560 | 1607 | ||
| 1561 | core_initcall(sysctl_init); | 1608 | core_initcall(sysctl_init); |
| 1562 | 1609 | ||
| 1563 | /** | 1610 | /** |
| 1564 | * register_sysctl_paths - register a sysctl hierarchy | 1611 | * __register_sysctl_paths - register a sysctl hierarchy |
| 1612 | * @root: List of sysctl headers to register on | ||
| 1613 | * @namespaces: Data to compute which lists of sysctl entries are visible | ||
| 1565 | * @path: The path to the directory the sysctl table is in. | 1614 | * @path: The path to the directory the sysctl table is in. |
| 1566 | * @table: the top-level table structure | 1615 | * @table: the top-level table structure |
| 1567 | * | 1616 | * |
| @@ -1629,9 +1678,12 @@ core_initcall(sysctl_init); | |||
| 1629 | * This routine returns %NULL on a failure to register, and a pointer | 1678 | * This routine returns %NULL on a failure to register, and a pointer |
| 1630 | * to the table header on success. | 1679 | * to the table header on success. |
| 1631 | */ | 1680 | */ |
| 1632 | struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, | 1681 | struct ctl_table_header *__register_sysctl_paths( |
| 1633 | struct ctl_table *table) | 1682 | struct ctl_table_root *root, |
| 1683 | struct nsproxy *namespaces, | ||
| 1684 | const struct ctl_path *path, struct ctl_table *table) | ||
| 1634 | { | 1685 | { |
| 1686 | struct list_head *header_list; | ||
| 1635 | struct ctl_table_header *header; | 1687 | struct ctl_table_header *header; |
| 1636 | struct ctl_table *new, **prevp; | 1688 | struct ctl_table *new, **prevp; |
| 1637 | unsigned int n, npath; | 1689 | unsigned int n, npath; |
| @@ -1674,19 +1726,38 @@ struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, | |||
| 1674 | INIT_LIST_HEAD(&header->ctl_entry); | 1726 | INIT_LIST_HEAD(&header->ctl_entry); |
| 1675 | header->used = 0; | 1727 | header->used = 0; |
| 1676 | header->unregistering = NULL; | 1728 | header->unregistering = NULL; |
| 1729 | header->root = root; | ||
| 1677 | sysctl_set_parent(NULL, header->ctl_table); | 1730 | sysctl_set_parent(NULL, header->ctl_table); |
| 1678 | if (sysctl_check_table(header->ctl_table)) { | 1731 | if (sysctl_check_table(namespaces, header->ctl_table)) { |
| 1679 | kfree(header); | 1732 | kfree(header); |
| 1680 | return NULL; | 1733 | return NULL; |
| 1681 | } | 1734 | } |
| 1682 | spin_lock(&sysctl_lock); | 1735 | spin_lock(&sysctl_lock); |
| 1683 | list_add_tail(&header->ctl_entry, &root_table_header.ctl_entry); | 1736 | header_list = lookup_header_list(root, namespaces); |
| 1737 | list_add_tail(&header->ctl_entry, header_list); | ||
| 1684 | spin_unlock(&sysctl_lock); | 1738 | spin_unlock(&sysctl_lock); |
| 1685 | 1739 | ||
| 1686 | return header; | 1740 | return header; |
| 1687 | } | 1741 | } |
| 1688 | 1742 | ||
| 1689 | /** | 1743 | /** |
| 1744 | * register_sysctl_table_path - register a sysctl table hierarchy | ||
| 1745 | * @path: The path to the directory the sysctl table is in. | ||
| 1746 | * @table: the top-level table structure | ||
| 1747 | * | ||
| 1748 | * Register a sysctl table hierarchy. @table should be a filled in ctl_table | ||
| 1749 | * array. A completely 0 filled entry terminates the table. | ||
| 1750 | * | ||
| 1751 | * See __register_sysctl_paths for more details. | ||
| 1752 | */ | ||
| 1753 | struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path, | ||
| 1754 | struct ctl_table *table) | ||
| 1755 | { | ||
| 1756 | return __register_sysctl_paths(&sysctl_table_root, current->nsproxy, | ||
| 1757 | path, table); | ||
| 1758 | } | ||
| 1759 | |||
| 1760 | /** | ||
| 1690 | * register_sysctl_table - register a sysctl table hierarchy | 1761 | * register_sysctl_table - register a sysctl table hierarchy |
| 1691 | * @table: the top-level table structure | 1762 | * @table: the top-level table structure |
| 1692 | * | 1763 | * |
diff --git a/kernel/sysctl_check.c b/kernel/sysctl_check.c index d8a5558a47b4..c3206fa50048 100644 --- a/kernel/sysctl_check.c +++ b/kernel/sysctl_check.c | |||
| @@ -1342,7 +1342,8 @@ static void sysctl_repair_table(struct ctl_table *table) | |||
| 1342 | } | 1342 | } |
| 1343 | } | 1343 | } |
| 1344 | 1344 | ||
| 1345 | static struct ctl_table *sysctl_check_lookup(struct ctl_table *table) | 1345 | static struct ctl_table *sysctl_check_lookup(struct nsproxy *namespaces, |
| 1346 | struct ctl_table *table) | ||
| 1346 | { | 1347 | { |
| 1347 | struct ctl_table_header *head; | 1348 | struct ctl_table_header *head; |
| 1348 | struct ctl_table *ref, *test; | 1349 | struct ctl_table *ref, *test; |
| @@ -1350,8 +1351,8 @@ static struct ctl_table *sysctl_check_lookup(struct ctl_table *table) | |||
| 1350 | 1351 | ||
| 1351 | depth = sysctl_depth(table); | 1352 | depth = sysctl_depth(table); |
| 1352 | 1353 | ||
| 1353 | for (head = sysctl_head_next(NULL); head; | 1354 | for (head = __sysctl_head_next(namespaces, NULL); head; |
| 1354 | head = sysctl_head_next(head)) { | 1355 | head = __sysctl_head_next(namespaces, head)) { |
| 1355 | cur_depth = depth; | 1356 | cur_depth = depth; |
| 1356 | ref = head->ctl_table; | 1357 | ref = head->ctl_table; |
| 1357 | repeat: | 1358 | repeat: |
| @@ -1396,13 +1397,14 @@ static void set_fail(const char **fail, struct ctl_table *table, const char *str | |||
| 1396 | *fail = str; | 1397 | *fail = str; |
| 1397 | } | 1398 | } |
| 1398 | 1399 | ||
| 1399 | static int sysctl_check_dir(struct ctl_table *table) | 1400 | static int sysctl_check_dir(struct nsproxy *namespaces, |
| 1401 | struct ctl_table *table) | ||
| 1400 | { | 1402 | { |
| 1401 | struct ctl_table *ref; | 1403 | struct ctl_table *ref; |
| 1402 | int error; | 1404 | int error; |
| 1403 | 1405 | ||
| 1404 | error = 0; | 1406 | error = 0; |
| 1405 | ref = sysctl_check_lookup(table); | 1407 | ref = sysctl_check_lookup(namespaces, table); |
| 1406 | if (ref) { | 1408 | if (ref) { |
| 1407 | int match = 0; | 1409 | int match = 0; |
| 1408 | if ((!table->procname && !ref->procname) || | 1410 | if ((!table->procname && !ref->procname) || |
| @@ -1427,11 +1429,12 @@ static int sysctl_check_dir(struct ctl_table *table) | |||
| 1427 | return error; | 1429 | return error; |
| 1428 | } | 1430 | } |
| 1429 | 1431 | ||
| 1430 | static void sysctl_check_leaf(struct ctl_table *table, const char **fail) | 1432 | static void sysctl_check_leaf(struct nsproxy *namespaces, |
| 1433 | struct ctl_table *table, const char **fail) | ||
| 1431 | { | 1434 | { |
| 1432 | struct ctl_table *ref; | 1435 | struct ctl_table *ref; |
| 1433 | 1436 | ||
| 1434 | ref = sysctl_check_lookup(table); | 1437 | ref = sysctl_check_lookup(namespaces, table); |
| 1435 | if (ref && (ref != table)) | 1438 | if (ref && (ref != table)) |
| 1436 | set_fail(fail, table, "Sysctl already exists"); | 1439 | set_fail(fail, table, "Sysctl already exists"); |
| 1437 | } | 1440 | } |
| @@ -1455,7 +1458,7 @@ static void sysctl_check_bin_path(struct ctl_table *table, const char **fail) | |||
| 1455 | } | 1458 | } |
| 1456 | } | 1459 | } |
| 1457 | 1460 | ||
| 1458 | int sysctl_check_table(struct ctl_table *table) | 1461 | int sysctl_check_table(struct nsproxy *namespaces, struct ctl_table *table) |
| 1459 | { | 1462 | { |
| 1460 | int error = 0; | 1463 | int error = 0; |
| 1461 | for (; table->ctl_name || table->procname; table++) { | 1464 | for (; table->ctl_name || table->procname; table++) { |
| @@ -1485,7 +1488,7 @@ int sysctl_check_table(struct ctl_table *table) | |||
| 1485 | set_fail(&fail, table, "Directory with extra1"); | 1488 | set_fail(&fail, table, "Directory with extra1"); |
| 1486 | if (table->extra2) | 1489 | if (table->extra2) |
| 1487 | set_fail(&fail, table, "Directory with extra2"); | 1490 | set_fail(&fail, table, "Directory with extra2"); |
| 1488 | if (sysctl_check_dir(table)) | 1491 | if (sysctl_check_dir(namespaces, table)) |
| 1489 | set_fail(&fail, table, "Inconsistent directory names"); | 1492 | set_fail(&fail, table, "Inconsistent directory names"); |
| 1490 | } else { | 1493 | } else { |
| 1491 | if ((table->strategy == sysctl_data) || | 1494 | if ((table->strategy == sysctl_data) || |
| @@ -1534,7 +1537,7 @@ int sysctl_check_table(struct ctl_table *table) | |||
| 1534 | if (!table->procname && table->proc_handler) | 1537 | if (!table->procname && table->proc_handler) |
| 1535 | set_fail(&fail, table, "proc_handler without procname"); | 1538 | set_fail(&fail, table, "proc_handler without procname"); |
| 1536 | #endif | 1539 | #endif |
| 1537 | sysctl_check_leaf(table, &fail); | 1540 | sysctl_check_leaf(namespaces, table, &fail); |
| 1538 | } | 1541 | } |
| 1539 | sysctl_check_bin_path(table, &fail); | 1542 | sysctl_check_bin_path(table, &fail); |
| 1540 | if (fail) { | 1543 | if (fail) { |
| @@ -1542,7 +1545,7 @@ int sysctl_check_table(struct ctl_table *table) | |||
| 1542 | error = -EINVAL; | 1545 | error = -EINVAL; |
| 1543 | } | 1546 | } |
| 1544 | if (table->child) | 1547 | if (table->child) |
| 1545 | error |= sysctl_check_table(table->child); | 1548 | error |= sysctl_check_table(namespaces, table->child); |
| 1546 | } | 1549 | } |
| 1547 | return error; | 1550 | return error; |
| 1548 | } | 1551 | } |
