Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'sysctl-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux

Pull sysctl updates from Luis Chamberlain:
"Long ago we set out to remove the kitchen sink on kernel/sysctl.c
arrays and placings sysctls to their own sybsystem or file to help
avoid merge conflicts. Matthew Wilcox pointed out though that if we're
going to do that we might as well also *save* space while at it and
try to remove the extra last sysctl entry added at the end of each
array, a sentintel, instead of bloating the kernel by adding a new
sentinel with each array moved.

Doing that was not so trivial, and has required slowing down the moves
of kernel/sysctl.c arrays and measuring the impact on size by each new
move.

The complex part of the effort to help reduce the size of each sysctl
is being done by the patient work of el señor Don Joel Granados. A lot
of this is truly painful code refactoring and testing and then trying
to measure the savings of each move and removing the sentinels.
Although Joel already has code which does most of this work,
experience with sysctl moves in the past shows is we need to be
careful due to the slew of odd build failures that are possible due to
the amount of random Kconfig options sysctls use.

To that end Joel's work is split by first addressing the major
housekeeping needed to remove the sentinels, which is part of this
merge request. The rest of the work to actually remove the sentinels
will be done later in future kernel releases.

The preliminary math is showing this will all help reduce the overall
build time size of the kernel and run time memory consumed by the
kernel by about ~64 bytes per array where we are able to remove each
sentinel in the future. That also means there is no more bloating the
kernel with the extra ~64 bytes per array moved as no new sentinels
are created"

* tag 'sysctl-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mcgrof/linux:
sysctl: Use ctl_table_size as stopping criteria for list macro
sysctl: SIZE_MAX->ARRAY_SIZE in register_net_sysctl
vrf: Update to register_net_sysctl_sz
networking: Update to register_net_sysctl_sz
netfilter: Update to register_net_sysctl_sz
ax.25: Update to register_net_sysctl_sz
sysctl: Add size to register_net_sysctl function
sysctl: Add size arg to __register_sysctl_init
sysctl: Add size to register_sysctl
sysctl: Add a size arg to __register_sysctl_table
sysctl: Add size argument to init_header
sysctl: Add ctl_table_size to ctl_table_header
sysctl: Use ctl_table_header in list_for_each_table_entry
sysctl: Prefer ctl_table_header in proc_sysctl

+223 -114
+1 -1
arch/arm64/kernel/armv8_deprecated.c
··· 569 569 sysctl->extra2 = &insn->max; 570 570 sysctl->proc_handler = emulation_proc_handler; 571 571 572 - register_sysctl("abi", sysctl); 572 + register_sysctl_sz("abi", sysctl, 1); 573 573 } 574 574 } 575 575
+1 -1
arch/s390/appldata/appldata_base.c
··· 365 365 ops->ctl_table[0].proc_handler = appldata_generic_handler; 366 366 ops->ctl_table[0].data = ops; 367 367 368 - ops->sysctl_header = register_sysctl(appldata_proc_name, ops->ctl_table); 368 + ops->sysctl_header = register_sysctl_sz(appldata_proc_name, ops->ctl_table, 1); 369 369 if (!ops->sysctl_header) 370 370 goto out; 371 371 return 0;
+2 -1
drivers/net/vrf.c
··· 1977 1977 /* init the extra1 parameter with the reference to current netns */ 1978 1978 table[0].extra1 = net; 1979 1979 1980 - nn_vrf->ctl_hdr = register_net_sysctl(net, "net/vrf", table); 1980 + nn_vrf->ctl_hdr = register_net_sysctl_sz(net, "net/vrf", table, 1981 + ARRAY_SIZE(vrf_table)); 1981 1982 if (!nn_vrf->ctl_hdr) { 1982 1983 kfree(table); 1983 1984 return -ENOMEM;
+46 -44
fs/proc/proc_sysctl.c
··· 19 19 #include <linux/kmemleak.h> 20 20 #include "internal.h" 21 21 22 - #define list_for_each_table_entry(entry, table) \ 23 - for ((entry) = (table); (entry)->procname; (entry)++) 22 + #define list_for_each_table_entry(entry, header) \ 23 + entry = header->ctl_table; \ 24 + for (size_t i = 0 ; i < header->ctl_table_size && entry->procname; ++i, entry++) 24 25 25 26 static const struct dentry_operations proc_sys_dentry_operations; 26 27 static const struct file_operations proc_sys_file_operations; ··· 44 43 */ 45 44 struct ctl_table_header *register_sysctl_mount_point(const char *path) 46 45 { 47 - return register_sysctl(path, sysctl_mount_point); 46 + return register_sysctl_sz(path, sysctl_mount_point, 0); 48 47 } 49 48 EXPORT_SYMBOL(register_sysctl_mount_point); 50 49 ··· 189 188 190 189 static void init_header(struct ctl_table_header *head, 191 190 struct ctl_table_root *root, struct ctl_table_set *set, 192 - struct ctl_node *node, struct ctl_table *table) 191 + struct ctl_node *node, struct ctl_table *table, size_t table_size) 193 192 { 194 193 head->ctl_table = table; 194 + head->ctl_table_size = table_size; 195 195 head->ctl_table_arg = table; 196 196 head->used = 0; 197 197 head->count = 1; ··· 206 204 if (node) { 207 205 struct ctl_table *entry; 208 206 209 - list_for_each_table_entry(entry, table) { 207 + list_for_each_table_entry(entry, head) { 210 208 node->header = head; 211 209 node++; 212 210 } ··· 217 215 { 218 216 struct ctl_table *entry; 219 217 220 - list_for_each_table_entry(entry, head->ctl_table) 218 + list_for_each_table_entry(entry, head) 221 219 erase_entry(head, entry); 222 220 } 223 221 ··· 244 242 err = insert_links(header); 245 243 if (err) 246 244 goto fail_links; 247 - list_for_each_table_entry(entry, header->ctl_table) { 245 + list_for_each_table_entry(entry, header) { 248 246 err = insert_entry(header, entry); 249 247 if (err) 250 248 goto fail; ··· 975 973 memcpy(new_name, name, namelen); 976 974 table[0].procname = new_name; 977 975 table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO; 978 - init_header(&new->header, set->dir.header.root, set, node, table); 976 + init_header(&new->header, set->dir.header.root, set, node, table, 1); 979 977 980 978 return new; 981 979 } ··· 1127 1125 return err; 1128 1126 } 1129 1127 1130 - static int sysctl_check_table(const char *path, struct ctl_table *table) 1128 + static int sysctl_check_table(const char *path, struct ctl_table_header *header) 1131 1129 { 1132 1130 struct ctl_table *entry; 1133 1131 int err = 0; 1134 - list_for_each_table_entry(entry, table) { 1132 + list_for_each_table_entry(entry, header) { 1135 1133 if ((entry->proc_handler == proc_dostring) || 1136 1134 (entry->proc_handler == proc_dobool) || 1137 1135 (entry->proc_handler == proc_dointvec) || ··· 1161 1159 return err; 1162 1160 } 1163 1161 1164 - static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table *table, 1165 - struct ctl_table_root *link_root) 1162 + static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_header *head) 1166 1163 { 1167 1164 struct ctl_table *link_table, *entry, *link; 1168 1165 struct ctl_table_header *links; ··· 1171 1170 1172 1171 name_bytes = 0; 1173 1172 nr_entries = 0; 1174 - list_for_each_table_entry(entry, table) { 1173 + list_for_each_table_entry(entry, head) { 1175 1174 nr_entries++; 1176 1175 name_bytes += strlen(entry->procname) + 1; 1177 1176 } ··· 1190 1189 link_name = (char *)&link_table[nr_entries + 1]; 1191 1190 link = link_table; 1192 1191 1193 - list_for_each_table_entry(entry, table) { 1192 + list_for_each_table_entry(entry, head) { 1194 1193 int len = strlen(entry->procname) + 1; 1195 1194 memcpy(link_name, entry->procname, len); 1196 1195 link->procname = link_name; 1197 1196 link->mode = S_IFLNK|S_IRWXUGO; 1198 - link->data = link_root; 1197 + link->data = head->root; 1199 1198 link_name += len; 1200 1199 link++; 1201 1200 } 1202 - init_header(links, dir->header.root, dir->header.set, node, link_table); 1201 + init_header(links, dir->header.root, dir->header.set, node, link_table, 1202 + head->ctl_table_size); 1203 1203 links->nreg = nr_entries; 1204 1204 1205 1205 return links; 1206 1206 } 1207 1207 1208 1208 static bool get_links(struct ctl_dir *dir, 1209 - struct ctl_table *table, struct ctl_table_root *link_root) 1209 + struct ctl_table_header *header, 1210 + struct ctl_table_root *link_root) 1210 1211 { 1211 - struct ctl_table_header *head; 1212 + struct ctl_table_header *tmp_head; 1212 1213 struct ctl_table *entry, *link; 1213 1214 1214 1215 /* Are there links available for every entry in table? */ 1215 - list_for_each_table_entry(entry, table) { 1216 + list_for_each_table_entry(entry, header) { 1216 1217 const char *procname = entry->procname; 1217 - link = find_entry(&head, dir, procname, strlen(procname)); 1218 + link = find_entry(&tmp_head, dir, procname, strlen(procname)); 1218 1219 if (!link) 1219 1220 return false; 1220 1221 if (S_ISDIR(link->mode) && S_ISDIR(entry->mode)) ··· 1227 1224 } 1228 1225 1229 1226 /* The checks passed. Increase the registration count on the links */ 1230 - list_for_each_table_entry(entry, table) { 1227 + list_for_each_table_entry(entry, header) { 1231 1228 const char *procname = entry->procname; 1232 - link = find_entry(&head, dir, procname, strlen(procname)); 1233 - head->nreg++; 1229 + link = find_entry(&tmp_head, dir, procname, strlen(procname)); 1230 + tmp_head->nreg++; 1234 1231 } 1235 1232 return true; 1236 1233 } ··· 1249 1246 if (IS_ERR(core_parent)) 1250 1247 return 0; 1251 1248 1252 - if (get_links(core_parent, head->ctl_table, head->root)) 1249 + if (get_links(core_parent, head, head->root)) 1253 1250 return 0; 1254 1251 1255 1252 core_parent->header.nreg++; 1256 1253 spin_unlock(&sysctl_lock); 1257 1254 1258 - links = new_links(core_parent, head->ctl_table, head->root); 1255 + links = new_links(core_parent, head); 1259 1256 1260 1257 spin_lock(&sysctl_lock); 1261 1258 err = -ENOMEM; ··· 1263 1260 goto out; 1264 1261 1265 1262 err = 0; 1266 - if (get_links(core_parent, head->ctl_table, head->root)) { 1263 + if (get_links(core_parent, head, head->root)) { 1267 1264 kfree(links); 1268 1265 goto out; 1269 1266 } ··· 1313 1310 * should not be free'd after registration. So it should not be 1314 1311 * used on stack. It can either be a global or dynamically allocated 1315 1312 * by the caller and free'd later after sysctl unregistration. 1313 + * @table_size : The number of elements in table 1316 1314 * 1317 1315 * Register a sysctl table hierarchy. @table should be a filled in ctl_table 1318 1316 * array. A completely 0 filled entry terminates the table. ··· 1356 1352 */ 1357 1353 struct ctl_table_header *__register_sysctl_table( 1358 1354 struct ctl_table_set *set, 1359 - const char *path, struct ctl_table *table) 1355 + const char *path, struct ctl_table *table, size_t table_size) 1360 1356 { 1361 1357 struct ctl_table_root *root = set->dir.header.root; 1362 1358 struct ctl_table_header *header; 1363 1359 struct ctl_dir *dir; 1364 - struct ctl_table *entry; 1365 1360 struct ctl_node *node; 1366 - int nr_entries = 0; 1367 - 1368 - list_for_each_table_entry(entry, table) 1369 - nr_entries++; 1370 1361 1371 1362 header = kzalloc(sizeof(struct ctl_table_header) + 1372 - sizeof(struct ctl_node)*nr_entries, GFP_KERNEL_ACCOUNT); 1363 + sizeof(struct ctl_node)*table_size, GFP_KERNEL_ACCOUNT); 1373 1364 if (!header) 1374 1365 return NULL; 1375 1366 1376 1367 node = (struct ctl_node *)(header + 1); 1377 - init_header(header, root, set, node, table); 1378 - if (sysctl_check_table(path, table)) 1368 + init_header(header, root, set, node, table, table_size); 1369 + if (sysctl_check_table(path, header)) 1379 1370 goto fail; 1380 1371 1381 1372 spin_lock(&sysctl_lock); ··· 1400 1401 } 1401 1402 1402 1403 /** 1403 - * register_sysctl - register a sysctl table 1404 + * register_sysctl_sz - register a sysctl table 1404 1405 * @path: The path to the directory the sysctl table is in. If the path 1405 1406 * doesn't exist we will create it for you. 1406 1407 * @table: the table structure. The calller must ensure the life of the @table ··· 1410 1411 * to call unregister_sysctl_table() and can instead use something like 1411 1412 * register_sysctl_init() which does not care for the result of the syctl 1412 1413 * registration. 1414 + * @table_size: The number of elements in table. 1413 1415 * 1414 1416 * Register a sysctl table. @table should be a filled in ctl_table 1415 1417 * array. A completely 0 filled entry terminates the table. 1416 1418 * 1417 1419 * See __register_sysctl_table for more details. 1418 1420 */ 1419 - struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table) 1421 + struct ctl_table_header *register_sysctl_sz(const char *path, struct ctl_table *table, 1422 + size_t table_size) 1420 1423 { 1421 1424 return __register_sysctl_table(&sysctl_table_root.default_set, 1422 - path, table); 1425 + path, table, table_size); 1423 1426 } 1424 - EXPORT_SYMBOL(register_sysctl); 1427 + EXPORT_SYMBOL(register_sysctl_sz); 1425 1428 1426 1429 /** 1427 1430 * __register_sysctl_init() - register sysctl table to path ··· 1434 1433 * lifetime use of the sysctl. 1435 1434 * @table_name: The name of sysctl table, only used for log printing when 1436 1435 * registration fails 1436 + * @table_size: The number of elements in table 1437 1437 * 1438 1438 * The sysctl interface is used by userspace to query or modify at runtime 1439 1439 * a predefined value set on a variable. These variables however have default ··· 1447 1445 * Context: if your base directory does not exist it will be created for you. 1448 1446 */ 1449 1447 void __init __register_sysctl_init(const char *path, struct ctl_table *table, 1450 - const char *table_name) 1448 + const char *table_name, size_t table_size) 1451 1449 { 1452 - struct ctl_table_header *hdr = register_sysctl(path, table); 1450 + struct ctl_table_header *hdr = register_sysctl_sz(path, table, table_size); 1453 1451 1454 1452 if (unlikely(!hdr)) { 1455 - pr_err("failed when register_sysctl %s to %s\n", table_name, path); 1453 + pr_err("failed when register_sysctl_sz %s to %s\n", table_name, path); 1456 1454 return; 1457 1455 } 1458 1456 kmemleak_not_leak(hdr); ··· 1473 1471 if (IS_ERR(core_parent)) 1474 1472 return; 1475 1473 1476 - list_for_each_table_entry(entry, header->ctl_table) { 1474 + list_for_each_table_entry(entry, header) { 1477 1475 struct ctl_table_header *link_head; 1478 1476 struct ctl_table *link; 1479 1477 const char *name = entry->procname; ··· 1537 1535 { 1538 1536 memset(set, 0, sizeof(*set)); 1539 1537 set->is_seen = is_seen; 1540 - init_header(&set->dir.header, root, set, NULL, root_table); 1538 + init_header(&set->dir.header, root, set, NULL, root_table, 1); 1541 1539 } 1542 1540 1543 1541 void retire_sysctl_set(struct ctl_table_set *set)
+24 -7
include/linux/sysctl.h
··· 159 159 struct ctl_table_header *header; 160 160 }; 161 161 162 - /* struct ctl_table_header is used to maintain dynamic lists of 163 - struct ctl_table trees. */ 162 + /** 163 + * struct ctl_table_header - maintains dynamic lists of struct ctl_table trees 164 + * @ctl_table: pointer to the first element in ctl_table array 165 + * @ctl_table_size: number of elements pointed by @ctl_table 166 + * @used: The entry will never be touched when equal to 0. 167 + * @count: Upped every time something is added to @inodes and downed every time 168 + * something is removed from inodes 169 + * @nreg: When nreg drops to 0 the ctl_table_header will be unregistered. 170 + * @rcu: Delays the freeing of the inode. Introduced with "unfuck proc_sysctl ->d_compare()" 171 + * 172 + */ 164 173 struct ctl_table_header { 165 174 union { 166 175 struct { 167 176 struct ctl_table *ctl_table; 177 + int ctl_table_size; 168 178 int used; 169 179 int count; 170 180 int nreg; ··· 215 205 const char *procname; 216 206 }; 217 207 208 + #define register_sysctl(path, table) \ 209 + register_sysctl_sz(path, table, ARRAY_SIZE(table)) 210 + 218 211 #ifdef CONFIG_SYSCTL 219 212 220 213 void proc_sys_poll_notify(struct ctl_table_poll *poll); ··· 229 216 230 217 struct ctl_table_header *__register_sysctl_table( 231 218 struct ctl_table_set *set, 232 - const char *path, struct ctl_table *table); 233 - struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table); 219 + const char *path, struct ctl_table *table, size_t table_size); 220 + struct ctl_table_header *register_sysctl_sz(const char *path, struct ctl_table *table, 221 + size_t table_size); 234 222 void unregister_sysctl_table(struct ctl_table_header * table); 235 223 236 224 extern int sysctl_init_bases(void); 237 225 extern void __register_sysctl_init(const char *path, struct ctl_table *table, 238 - const char *table_name); 239 - #define register_sysctl_init(path, table) __register_sysctl_init(path, table, #table) 226 + const char *table_name, size_t table_size); 227 + #define register_sysctl_init(path, table) \ 228 + __register_sysctl_init(path, table, #table, ARRAY_SIZE(table)) 240 229 extern struct ctl_table_header *register_sysctl_mount_point(const char *path); 241 230 242 231 void do_sysctl_args(void); ··· 267 252 return NULL; 268 253 } 269 254 270 - static inline struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *table) 255 + static inline struct ctl_table_header *register_sysctl_sz(const char *path, 256 + struct ctl_table *table, 257 + size_t table_size) 271 258 { 272 259 return NULL; 273 260 }
+2
include/net/ipv6.h
··· 1272 1272 1273 1273 #ifdef CONFIG_SYSCTL 1274 1274 struct ctl_table *ipv6_icmp_sysctl_init(struct net *net); 1275 + size_t ipv6_icmp_sysctl_table_size(void); 1275 1276 struct ctl_table *ipv6_route_sysctl_init(struct net *net); 1277 + size_t ipv6_route_sysctl_table_size(struct net *net); 1276 1278 int ipv6_sysctl_register(void); 1277 1279 void ipv6_sysctl_unregister(void); 1278 1280 #endif
+6 -4
include/net/net_namespace.h
··· 471 471 472 472 struct ctl_table; 473 473 474 + #define register_net_sysctl(net, path, table) \ 475 + register_net_sysctl_sz(net, path, table, ARRAY_SIZE(table)) 474 476 #ifdef CONFIG_SYSCTL 475 477 int net_sysctl_init(void); 476 - struct ctl_table_header *register_net_sysctl(struct net *net, const char *path, 477 - struct ctl_table *table); 478 + struct ctl_table_header *register_net_sysctl_sz(struct net *net, const char *path, 479 + struct ctl_table *table, size_t table_size); 478 480 void unregister_net_sysctl_table(struct ctl_table_header *header); 479 481 #else 480 482 static inline int net_sysctl_init(void) { return 0; } 481 - static inline struct ctl_table_header *register_net_sysctl(struct net *net, 482 - const char *path, struct ctl_table *table) 483 + static inline struct ctl_table_header *register_net_sysctl_sz(struct net *net, 484 + const char *path, struct ctl_table *table, size_t table_size) 483 485 { 484 486 return NULL; 485 487 }
+3 -1
ipc/ipc_sysctl.c
··· 259 259 tbl[i].data = NULL; 260 260 } 261 261 262 - ns->ipc_sysctls = __register_sysctl_table(&ns->ipc_set, "kernel", tbl); 262 + ns->ipc_sysctls = __register_sysctl_table(&ns->ipc_set, 263 + "kernel", tbl, 264 + ARRAY_SIZE(ipc_sysctls)); 263 265 } 264 266 if (!ns->ipc_sysctls) { 265 267 kfree(tbl);
+3 -1
ipc/mq_sysctl.c
··· 109 109 tbl[i].data = NULL; 110 110 } 111 111 112 - ns->mq_sysctls = __register_sysctl_table(&ns->mq_set, "fs/mqueue", tbl); 112 + ns->mq_sysctls = __register_sysctl_table(&ns->mq_set, 113 + "fs/mqueue", tbl, 114 + ARRAY_SIZE(mq_sysctls)); 113 115 } 114 116 if (!ns->mq_sysctls) { 115 117 kfree(tbl);
+3 -2
kernel/ucount.c
··· 104 104 for (i = 0; i < UCOUNT_COUNTS; i++) { 105 105 tbl[i].data = &ns->ucount_max[i]; 106 106 } 107 - ns->sysctls = __register_sysctl_table(&ns->set, "user", tbl); 107 + ns->sysctls = __register_sysctl_table(&ns->set, "user", tbl, 108 + ARRAY_SIZE(user_table)); 108 109 } 109 110 if (!ns->sysctls) { 110 111 kfree(tbl); ··· 365 364 * default set so that registrations in the child sets work 366 365 * properly. 367 366 */ 368 - user_header = register_sysctl("user", empty); 367 + user_header = register_sysctl_sz("user", empty, 0); 369 368 kmemleak_ignore(user_header); 370 369 BUG_ON(!user_header); 371 370 BUG_ON(!setup_userns_sysctls(&init_user_ns));
+2 -1
net/ax25/sysctl_net_ax25.c
··· 159 159 table[k].data = &ax25_dev->values[k]; 160 160 161 161 snprintf(path, sizeof(path), "net/ax25/%s", ax25_dev->dev->name); 162 - ax25_dev->sysheader = register_net_sysctl(&init_net, path, table); 162 + ax25_dev->sysheader = register_net_sysctl_sz(&init_net, path, table, 163 + ARRAY_SIZE(ax25_param_table)); 163 164 if (!ax25_dev->sysheader) { 164 165 kfree(table); 165 166 return -ENOMEM;
+2 -1
net/bridge/br_netfilter_hooks.c
··· 1135 1135 1136 1136 br_netfilter_sysctl_default(brnet); 1137 1137 1138 - brnet->ctl_hdr = register_net_sysctl(net, "net/bridge", table); 1138 + brnet->ctl_hdr = register_net_sysctl_sz(net, "net/bridge", table, 1139 + ARRAY_SIZE(brnf_table)); 1139 1140 if (!brnet->ctl_hdr) { 1140 1141 if (!net_eq(net, &init_net)) 1141 1142 kfree(table);
+6 -2
net/core/neighbour.c
··· 3779 3779 const char *dev_name_source; 3780 3780 char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ]; 3781 3781 char *p_name; 3782 + size_t neigh_vars_size; 3782 3783 3783 3784 t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL_ACCOUNT); 3784 3785 if (!t) ··· 3791 3790 t->neigh_vars[i].extra2 = p; 3792 3791 } 3793 3792 3793 + neigh_vars_size = ARRAY_SIZE(t->neigh_vars); 3794 3794 if (dev) { 3795 3795 dev_name_source = dev->name; 3796 3796 /* Terminate the table early */ 3797 3797 memset(&t->neigh_vars[NEIGH_VAR_GC_INTERVAL], 0, 3798 3798 sizeof(t->neigh_vars[NEIGH_VAR_GC_INTERVAL])); 3799 + neigh_vars_size = NEIGH_VAR_BASE_REACHABLE_TIME_MS + 1; 3799 3800 } else { 3800 3801 struct neigh_table *tbl = p->tbl; 3801 3802 dev_name_source = "default"; ··· 3844 3841 3845 3842 snprintf(neigh_path, sizeof(neigh_path), "net/%s/neigh/%s", 3846 3843 p_name, dev_name_source); 3847 - t->sysctl_header = 3848 - register_net_sysctl(neigh_parms_net(p), neigh_path, t->neigh_vars); 3844 + t->sysctl_header = register_net_sysctl_sz(neigh_parms_net(p), 3845 + neigh_path, t->neigh_vars, 3846 + neigh_vars_size); 3849 3847 if (!t->sysctl_header) 3850 3848 goto free; 3851 3849
+2 -1
net/core/sysctl_net_core.c
··· 712 712 tmp->data += (char *)net - (char *)&init_net; 713 713 } 714 714 715 - net->core.sysctl_hdr = register_net_sysctl(net, "net/core", tbl); 715 + net->core.sysctl_hdr = register_net_sysctl_sz(net, "net/core", tbl, 716 + ARRAY_SIZE(netns_core_table)); 716 717 if (net->core.sysctl_hdr == NULL) 717 718 goto err_reg; 718 719
+6 -2
net/ieee802154/6lowpan/reassembly.c
··· 360 360 struct ctl_table_header *hdr; 361 361 struct netns_ieee802154_lowpan *ieee802154_lowpan = 362 362 net_ieee802154_lowpan(net); 363 + size_t table_size = ARRAY_SIZE(lowpan_frags_ns_ctl_table); 363 364 364 365 table = lowpan_frags_ns_ctl_table; 365 366 if (!net_eq(net, &init_net)) { ··· 370 369 goto err_alloc; 371 370 372 371 /* Don't export sysctls to unprivileged users */ 373 - if (net->user_ns != &init_user_ns) 372 + if (net->user_ns != &init_user_ns) { 374 373 table[0].procname = NULL; 374 + table_size = 0; 375 + } 375 376 } 376 377 377 378 table[0].data = &ieee802154_lowpan->fqdir->high_thresh; ··· 382 379 table[1].extra2 = &ieee802154_lowpan->fqdir->high_thresh; 383 380 table[2].data = &ieee802154_lowpan->fqdir->timeout; 384 381 385 - hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table); 382 + hdr = register_net_sysctl_sz(net, "net/ieee802154/6lowpan", table, 383 + table_size); 386 384 if (hdr == NULL) 387 385 goto err_reg; 388 386
+2 -1
net/ipv4/devinet.c
··· 2737 2737 goto err_reg_dflt; 2738 2738 2739 2739 err = -ENOMEM; 2740 - forw_hdr = register_net_sysctl(net, "net/ipv4", tbl); 2740 + forw_hdr = register_net_sysctl_sz(net, "net/ipv4", tbl, 2741 + ARRAY_SIZE(ctl_forward_entry)); 2741 2742 if (!forw_hdr) 2742 2743 goto err_reg_ctl; 2743 2744 net->ipv4.forw_hdr = forw_hdr;
+2 -1
net/ipv4/ip_fragment.c
··· 615 615 table[2].data = &net->ipv4.fqdir->timeout; 616 616 table[3].data = &net->ipv4.fqdir->max_dist; 617 617 618 - hdr = register_net_sysctl(net, "net/ipv4", table); 618 + hdr = register_net_sysctl_sz(net, "net/ipv4", table, 619 + ARRAY_SIZE(ip4_frags_ns_ctl_table)); 619 620 if (!hdr) 620 621 goto err_reg; 621 622
+6 -2
net/ipv4/route.c
··· 3592 3592 static __net_init int sysctl_route_net_init(struct net *net) 3593 3593 { 3594 3594 struct ctl_table *tbl; 3595 + size_t table_size = ARRAY_SIZE(ipv4_route_netns_table); 3595 3596 3596 3597 tbl = ipv4_route_netns_table; 3597 3598 if (!net_eq(net, &init_net)) { ··· 3604 3603 3605 3604 /* Don't export non-whitelisted sysctls to unprivileged users */ 3606 3605 if (net->user_ns != &init_user_ns) { 3607 - if (tbl[0].procname != ipv4_route_flush_procname) 3606 + if (tbl[0].procname != ipv4_route_flush_procname) { 3608 3607 tbl[0].procname = NULL; 3608 + table_size = 0; 3609 + } 3609 3610 } 3610 3611 3611 3612 /* Update the variables to point into the current struct net ··· 3618 3615 } 3619 3616 tbl[0].extra1 = net; 3620 3617 3621 - net->ipv4.route_hdr = register_net_sysctl(net, "net/ipv4/route", tbl); 3618 + net->ipv4.route_hdr = register_net_sysctl_sz(net, "net/ipv4/route", 3619 + tbl, table_size); 3622 3620 if (!net->ipv4.route_hdr) 3623 3621 goto err_reg; 3624 3622 return 0;
+2 -1
net/ipv4/sysctl_net_ipv4.c
··· 1519 1519 } 1520 1520 } 1521 1521 1522 - net->ipv4.ipv4_hdr = register_net_sysctl(net, "net/ipv4", table); 1522 + net->ipv4.ipv4_hdr = register_net_sysctl_sz(net, "net/ipv4", table, 1523 + ARRAY_SIZE(ipv4_net_table)); 1523 1524 if (!net->ipv4.ipv4_hdr) 1524 1525 goto err_reg; 1525 1526
+2 -1
net/ipv4/xfrm4_policy.c
··· 169 169 table[0].data = &net->xfrm.xfrm4_dst_ops.gc_thresh; 170 170 } 171 171 172 - hdr = register_net_sysctl(net, "net/ipv4", table); 172 + hdr = register_net_sysctl_sz(net, "net/ipv4", table, 173 + ARRAY_SIZE(xfrm4_policy_table)); 173 174 if (!hdr) 174 175 goto err_reg; 175 176
+2 -1
net/ipv6/addrconf.c
··· 7135 7135 7136 7136 snprintf(path, sizeof(path), "net/ipv6/conf/%s", dev_name); 7137 7137 7138 - p->sysctl_header = register_net_sysctl(net, path, table); 7138 + p->sysctl_header = register_net_sysctl_sz(net, path, table, 7139 + ARRAY_SIZE(addrconf_sysctl)); 7139 7140 if (!p->sysctl_header) 7140 7141 goto free; 7141 7142
+5
net/ipv6/icmp.c
··· 1227 1227 } 1228 1228 return table; 1229 1229 } 1230 + 1231 + size_t ipv6_icmp_sysctl_table_size(void) 1232 + { 1233 + return ARRAY_SIZE(ipv6_icmp_table_template); 1234 + } 1230 1235 #endif
+2 -1
net/ipv6/netfilter/nf_conntrack_reasm.c
··· 87 87 table[2].data = &nf_frag->fqdir->high_thresh; 88 88 table[2].extra1 = &nf_frag->fqdir->low_thresh; 89 89 90 - hdr = register_net_sysctl(net, "net/netfilter", table); 90 + hdr = register_net_sysctl_sz(net, "net/netfilter", table, 91 + ARRAY_SIZE(nf_ct_frag6_sysctl_table)); 91 92 if (hdr == NULL) 92 93 goto err_reg; 93 94
+2 -1
net/ipv6/reassembly.c
··· 470 470 table[1].extra2 = &net->ipv6.fqdir->high_thresh; 471 471 table[2].data = &net->ipv6.fqdir->timeout; 472 472 473 - hdr = register_net_sysctl(net, "net/ipv6", table); 473 + hdr = register_net_sysctl_sz(net, "net/ipv6", table, 474 + ARRAY_SIZE(ip6_frags_ns_ctl_table)); 474 475 if (!hdr) 475 476 goto err_reg; 476 477
+9
net/ipv6/route.c
··· 6453 6453 6454 6454 return table; 6455 6455 } 6456 + 6457 + size_t ipv6_route_sysctl_table_size(struct net *net) 6458 + { 6459 + /* Don't export sysctls to unprivileged users */ 6460 + if (net->user_ns != &init_user_ns) 6461 + return 1; 6462 + 6463 + return ARRAY_SIZE(ipv6_route_table_template); 6464 + } 6456 6465 #endif 6457 6466 6458 6467 static int __net_init ip6_route_net_init(struct net *net)
+11 -5
net/ipv6/sysctl_net_ipv6.c
··· 275 275 if (!ipv6_icmp_table) 276 276 goto out_ipv6_route_table; 277 277 278 - net->ipv6.sysctl.hdr = register_net_sysctl(net, "net/ipv6", ipv6_table); 278 + net->ipv6.sysctl.hdr = register_net_sysctl_sz(net, "net/ipv6", 279 + ipv6_table, 280 + ARRAY_SIZE(ipv6_table_template)); 279 281 if (!net->ipv6.sysctl.hdr) 280 282 goto out_ipv6_icmp_table; 281 283 282 - net->ipv6.sysctl.route_hdr = 283 - register_net_sysctl(net, "net/ipv6/route", ipv6_route_table); 284 + net->ipv6.sysctl.route_hdr = register_net_sysctl_sz(net, 285 + "net/ipv6/route", 286 + ipv6_route_table, 287 + ipv6_route_sysctl_table_size(net)); 284 288 if (!net->ipv6.sysctl.route_hdr) 285 289 goto out_unregister_ipv6_table; 286 290 287 - net->ipv6.sysctl.icmp_hdr = 288 - register_net_sysctl(net, "net/ipv6/icmp", ipv6_icmp_table); 291 + net->ipv6.sysctl.icmp_hdr = register_net_sysctl_sz(net, 292 + "net/ipv6/icmp", 293 + ipv6_icmp_table, 294 + ipv6_icmp_sysctl_table_size()); 289 295 if (!net->ipv6.sysctl.icmp_hdr) 290 296 goto out_unregister_route_table; 291 297
+2 -1
net/ipv6/xfrm6_policy.c
··· 201 201 table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh; 202 202 } 203 203 204 - hdr = register_net_sysctl(net, "net/ipv6", table); 204 + hdr = register_net_sysctl_sz(net, "net/ipv6", table, 205 + ARRAY_SIZE(xfrm6_policy_table)); 205 206 if (!hdr) 206 207 goto err_reg; 207 208
+4 -2
net/mpls/af_mpls.c
··· 1419 1419 1420 1420 snprintf(path, sizeof(path), "net/mpls/conf/%s", dev->name); 1421 1421 1422 - mdev->sysctl = register_net_sysctl(net, path, table); 1422 + mdev->sysctl = register_net_sysctl_sz(net, path, table, 1423 + ARRAY_SIZE(mpls_dev_table)); 1423 1424 if (!mdev->sysctl) 1424 1425 goto free; 1425 1426 ··· 2690 2689 for (i = 0; i < ARRAY_SIZE(mpls_table) - 1; i++) 2691 2690 table[i].data = (char *)net + (uintptr_t)table[i].data; 2692 2691 2693 - net->mpls.ctl = register_net_sysctl(net, "net/mpls", table); 2692 + net->mpls.ctl = register_net_sysctl_sz(net, "net/mpls", table, 2693 + ARRAY_SIZE(mpls_table)); 2694 2694 if (net->mpls.ctl == NULL) { 2695 2695 kfree(table); 2696 2696 return -ENOMEM;
+2 -1
net/mptcp/ctrl.c
··· 164 164 table[5].data = &pernet->pm_type; 165 165 table[6].data = &pernet->scheduler; 166 166 167 - hdr = register_net_sysctl(net, MPTCP_SYSCTL_PATH, table); 167 + hdr = register_net_sysctl_sz(net, MPTCP_SYSCTL_PATH, table, 168 + ARRAY_SIZE(mptcp_sysctl_table)); 168 169 if (!hdr) 169 170 goto err_reg; 170 171
+6 -2
net/netfilter/ipvs/ip_vs_ctl.c
··· 4269 4269 struct net *net = ipvs->net; 4270 4270 struct ctl_table *tbl; 4271 4271 int idx, ret; 4272 + size_t ctl_table_size = ARRAY_SIZE(vs_vars); 4272 4273 4273 4274 atomic_set(&ipvs->dropentry, 0); 4274 4275 spin_lock_init(&ipvs->dropentry_lock); ··· 4286 4285 return -ENOMEM; 4287 4286 4288 4287 /* Don't export sysctls to unprivileged users */ 4289 - if (net->user_ns != &init_user_ns) 4288 + if (net->user_ns != &init_user_ns) { 4290 4289 tbl[0].procname = NULL; 4290 + ctl_table_size = 0; 4291 + } 4291 4292 } else 4292 4293 tbl = vs_vars; 4293 4294 /* Initialize sysctl defaults */ ··· 4360 4357 #endif 4361 4358 4362 4359 ret = -ENOMEM; 4363 - ipvs->sysctl_hdr = register_net_sysctl(net, "net/ipv4/vs", tbl); 4360 + ipvs->sysctl_hdr = register_net_sysctl_sz(net, "net/ipv4/vs", tbl, 4361 + ctl_table_size); 4364 4362 if (!ipvs->sysctl_hdr) 4365 4363 goto err; 4366 4364 ipvs->sysctl_tbl = tbl;
+7 -3
net/netfilter/ipvs/ip_vs_lblc.c
··· 550 550 static int __net_init __ip_vs_lblc_init(struct net *net) 551 551 { 552 552 struct netns_ipvs *ipvs = net_ipvs(net); 553 + size_t vars_table_size = ARRAY_SIZE(vs_vars_table); 553 554 554 555 if (!ipvs) 555 556 return -ENOENT; ··· 563 562 return -ENOMEM; 564 563 565 564 /* Don't export sysctls to unprivileged users */ 566 - if (net->user_ns != &init_user_ns) 565 + if (net->user_ns != &init_user_ns) { 567 566 ipvs->lblc_ctl_table[0].procname = NULL; 567 + vars_table_size = 0; 568 + } 568 569 569 570 } else 570 571 ipvs->lblc_ctl_table = vs_vars_table; 571 572 ipvs->sysctl_lblc_expiration = DEFAULT_EXPIRATION; 572 573 ipvs->lblc_ctl_table[0].data = &ipvs->sysctl_lblc_expiration; 573 574 574 - ipvs->lblc_ctl_header = 575 - register_net_sysctl(net, "net/ipv4/vs", ipvs->lblc_ctl_table); 575 + ipvs->lblc_ctl_header = register_net_sysctl_sz(net, "net/ipv4/vs", 576 + ipvs->lblc_ctl_table, 577 + vars_table_size); 576 578 if (!ipvs->lblc_ctl_header) { 577 579 if (!net_eq(net, &init_net)) 578 580 kfree(ipvs->lblc_ctl_table);
+7 -3
net/netfilter/ipvs/ip_vs_lblcr.c
··· 736 736 static int __net_init __ip_vs_lblcr_init(struct net *net) 737 737 { 738 738 struct netns_ipvs *ipvs = net_ipvs(net); 739 + size_t vars_table_size = ARRAY_SIZE(vs_vars_table); 739 740 740 741 if (!ipvs) 741 742 return -ENOENT; ··· 749 748 return -ENOMEM; 750 749 751 750 /* Don't export sysctls to unprivileged users */ 752 - if (net->user_ns != &init_user_ns) 751 + if (net->user_ns != &init_user_ns) { 753 752 ipvs->lblcr_ctl_table[0].procname = NULL; 753 + vars_table_size = 0; 754 + } 754 755 } else 755 756 ipvs->lblcr_ctl_table = vs_vars_table; 756 757 ipvs->sysctl_lblcr_expiration = DEFAULT_EXPIRATION; 757 758 ipvs->lblcr_ctl_table[0].data = &ipvs->sysctl_lblcr_expiration; 758 759 759 - ipvs->lblcr_ctl_header = 760 - register_net_sysctl(net, "net/ipv4/vs", ipvs->lblcr_ctl_table); 760 + ipvs->lblcr_ctl_header = register_net_sysctl_sz(net, "net/ipv4/vs", 761 + ipvs->lblcr_ctl_table, 762 + vars_table_size); 761 763 if (!ipvs->lblcr_ctl_header) { 762 764 if (!net_eq(net, &init_net)) 763 765 kfree(ipvs->lblcr_ctl_table);
+3 -1
net/netfilter/nf_conntrack_standalone.c
··· 1106 1106 table[NF_SYSCTL_CT_BUCKETS].mode = 0444; 1107 1107 } 1108 1108 1109 - cnet->sysctl_header = register_net_sysctl(net, "net/netfilter", table); 1109 + cnet->sysctl_header = register_net_sysctl_sz(net, "net/netfilter", 1110 + table, 1111 + ARRAY_SIZE(nf_ct_sysctl_table)); 1110 1112 if (!cnet->sysctl_header) 1111 1113 goto out_unregister_netfilter; 1112 1114
+4 -3
net/netfilter/nf_log.c
··· 487 487 for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) 488 488 table[i].extra2 = net; 489 489 490 - net->nf.nf_log_dir_header = register_net_sysctl(net, 491 - "net/netfilter/nf_log", 492 - table); 490 + net->nf.nf_log_dir_header = register_net_sysctl_sz(net, 491 + "net/netfilter/nf_log", 492 + table, 493 + ARRAY_SIZE(nf_log_sysctl_table)); 493 494 if (!net->nf.nf_log_dir_header) 494 495 goto err_reg; 495 496
+2 -1
net/rds/tcp.c
··· 565 565 } 566 566 tbl[RDS_TCP_SNDBUF].data = &rtn->sndbuf_size; 567 567 tbl[RDS_TCP_RCVBUF].data = &rtn->rcvbuf_size; 568 - rtn->rds_tcp_sysctl = register_net_sysctl(net, "net/rds/tcp", tbl); 568 + rtn->rds_tcp_sysctl = register_net_sysctl_sz(net, "net/rds/tcp", tbl, 569 + ARRAY_SIZE(rds_tcp_sysctl_table)); 569 570 if (!rtn->rds_tcp_sysctl) { 570 571 pr_warn("could not register sysctl\n"); 571 572 err = -ENOMEM;
+3 -1
net/sctp/sysctl.c
··· 612 612 table[SCTP_PF_RETRANS_IDX].extra2 = &net->sctp.ps_retrans; 613 613 table[SCTP_PS_RETRANS_IDX].extra1 = &net->sctp.pf_retrans; 614 614 615 - net->sctp.sysctl_header = register_net_sysctl(net, "net/sctp", table); 615 + net->sctp.sysctl_header = register_net_sysctl_sz(net, "net/sctp", 616 + table, 617 + ARRAY_SIZE(sctp_net_table)); 616 618 if (net->sctp.sysctl_header == NULL) { 617 619 kfree(table); 618 620 return -ENOMEM;
+2 -1
net/smc/smc_sysctl.c
··· 87 87 table[i].data += (void *)net - (void *)&init_net; 88 88 } 89 89 90 - net->smc.smc_hdr = register_net_sysctl(net, "net/smc", table); 90 + net->smc.smc_hdr = register_net_sysctl_sz(net, "net/smc", table, 91 + ARRAY_SIZE(smc_table)); 91 92 if (!net->smc.smc_hdr) 92 93 goto err_reg; 93 94
+19 -9
net/sysctl_net.c
··· 101 101 * registering "/proc/sys/net" as an empty directory not in a 102 102 * network namespace. 103 103 */ 104 - net_header = register_sysctl("net", empty); 104 + net_header = register_sysctl_sz("net", empty, 0); 105 105 if (!net_header) 106 106 goto out; 107 107 ret = register_pernet_subsys(&sysctl_pernet_ops); ··· 122 122 * allocated. 123 123 */ 124 124 static void ensure_safe_net_sysctl(struct net *net, const char *path, 125 - struct ctl_table *table) 125 + struct ctl_table *table, size_t table_size) 126 126 { 127 127 struct ctl_table *ent; 128 128 129 129 pr_debug("Registering net sysctl (net %p): %s\n", net, path); 130 - for (ent = table; ent->procname; ent++) { 130 + ent = table; 131 + for (size_t i = 0; i < table_size && ent->procname; ent++, i++) { 131 132 unsigned long addr; 132 133 const char *where; 133 134 ··· 161 160 } 162 161 } 163 162 164 - struct ctl_table_header *register_net_sysctl(struct net *net, 165 - const char *path, struct ctl_table *table) 163 + struct ctl_table_header *register_net_sysctl_sz(struct net *net, 164 + const char *path, 165 + struct ctl_table *table, 166 + size_t table_size) 166 167 { 167 - if (!net_eq(net, &init_net)) 168 - ensure_safe_net_sysctl(net, path, table); 168 + int count; 169 + struct ctl_table *entry; 169 170 170 - return __register_sysctl_table(&net->sysctls, path, table); 171 + if (!net_eq(net, &init_net)) 172 + ensure_safe_net_sysctl(net, path, table, table_size); 173 + 174 + entry = table; 175 + for (count = 0 ; count < table_size && entry->procname; entry++, count++) 176 + ; 177 + 178 + return __register_sysctl_table(&net->sysctls, path, table, count); 171 179 } 172 - EXPORT_SYMBOL_GPL(register_net_sysctl); 180 + EXPORT_SYMBOL_GPL(register_net_sysctl_sz); 173 181 174 182 void unregister_net_sysctl_table(struct ctl_table_header *header) 175 183 {
+2 -1
net/unix/sysctl_net_unix.c
··· 36 36 table[0].data = &net->unx.sysctl_max_dgram_qlen; 37 37 } 38 38 39 - net->unx.ctl = register_net_sysctl(net, "net/unix", table); 39 + net->unx.ctl = register_net_sysctl_sz(net, "net/unix", table, 40 + ARRAY_SIZE(unix_table)); 40 41 if (net->unx.ctl == NULL) 41 42 goto err_reg; 42 43
+6 -2
net/xfrm/xfrm_sysctl.c
··· 44 44 int __net_init xfrm_sysctl_init(struct net *net) 45 45 { 46 46 struct ctl_table *table; 47 + size_t table_size = ARRAY_SIZE(xfrm_table); 47 48 48 49 __xfrm_sysctl_init(net); 49 50 ··· 57 56 table[3].data = &net->xfrm.sysctl_acq_expires; 58 57 59 58 /* Don't export sysctls to unprivileged users */ 60 - if (net->user_ns != &init_user_ns) 59 + if (net->user_ns != &init_user_ns) { 61 60 table[0].procname = NULL; 61 + table_size = 0; 62 + } 62 63 63 - net->xfrm.sysctl_hdr = register_net_sysctl(net, "net/core", table); 64 + net->xfrm.sysctl_hdr = register_net_sysctl_sz(net, "net/core", table, 65 + table_size); 64 66 if (!net->xfrm.sysctl_hdr) 65 67 goto out_register; 66 68 return 0;