From 98ca62ba9e2be5863c7d069f84f7166b45a5b2f4 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 2 Apr 2024 23:10:34 +0200 Subject: sysctl: always initialize i_uid/i_gid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always initialize i_uid/i_gid inside the sysfs core so set_ownership() can safely skip setting them. Commit 5ec27ec735ba ("fs/proc/proc_sysctl.c: fix the default values of i_uid/i_gid on /proc/sys inodes.") added defaults for i_uid/i_gid when set_ownership() was not implemented. It also missed adjusting net_ctl_set_ownership() to use the same default values in case the computation of a better value failed. Fixes: 5ec27ec735ba ("fs/proc/proc_sysctl.c: fix the default values of i_uid/i_gid on /proc/sys inodes.") Cc: stable@vger.kernel.org Signed-off-by: Thomas Weißschuh Signed-off-by: Joel Granados --- fs/proc/proc_sysctl.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index b1c2c0b82116..dd7b462387a0 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -476,12 +476,10 @@ static struct inode *proc_sys_make_inode(struct super_block *sb, make_empty_dir_inode(inode); } + inode->i_uid = GLOBAL_ROOT_UID; + inode->i_gid = GLOBAL_ROOT_GID; if (root->set_ownership) root->set_ownership(head, &inode->i_uid, &inode->i_gid); - else { - inode->i_uid = GLOBAL_ROOT_UID; - inode->i_gid = GLOBAL_ROOT_GID; - } return inode; } -- cgit v1.2.3-58-ga151 From b5ffbd1396885f76bf87e67d590a3ef063e6d831 Mon Sep 17 00:00:00 2001 From: Wen Yang Date: Fri, 19 Apr 2024 11:36:39 +0800 Subject: sysctl: move the extra1/2 boundary check of u8 to sysctl_check_table_array Move boundary checking for proc_dou8ved_minmax into module loading, thereby reporting errors in advance. And add a kunit test case ensuring the boundary check is done correctly. The boundary check in proc_dou8vec_minmax done to the extra elements in the ctl_table struct is currently performed at runtime. This allows buggy kernel modules to be loaded normally without any errors only to fail when used. This is a buggy example module: #include #include #include static struct ctl_table_header *_table_header = NULL; static unsigned char _data = 0; struct ctl_table table[] = { { .procname = "foo", .data = &_data, .maxlen = sizeof(u8), .mode = 0644, .proc_handler = proc_dou8vec_minmax, .extra1 = SYSCTL_ZERO, .extra2 = SYSCTL_ONE_THOUSAND, }, }; static int init_demo(void) { _table_header = register_sysctl("kernel", table); if (!_table_header) return -ENOMEM; return 0; } module_init(init_demo); MODULE_LICENSE("GPL"); And this is the result: # insmod test.ko # cat /proc/sys/kernel/foo cat: /proc/sys/kernel/foo: Invalid argument Suggested-by: Joel Granados Signed-off-by: Wen Yang Cc: Luis Chamberlain Cc: Kees Cook Cc: Joel Granados Cc: Eric W. Biederman Cc: Christian Brauner Cc: linux-kernel@vger.kernel.org Reviewed-by: Joel Granados Signed-off-by: Joel Granados --- fs/proc/proc_sysctl.c | 14 ++++++++++++++ kernel/sysctl-test.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ kernel/sysctl.c | 10 ++-------- 3 files changed, 65 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index dd7b462387a0..c467e36741d0 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1091,6 +1091,7 @@ static int sysctl_err(const char *path, struct ctl_table *table, char *fmt, ...) static int sysctl_check_table_array(const char *path, struct ctl_table *table) { + unsigned int extra; int err = 0; if ((table->proc_handler == proc_douintvec) || @@ -1102,6 +1103,19 @@ static int sysctl_check_table_array(const char *path, struct ctl_table *table) if (table->proc_handler == proc_dou8vec_minmax) { if (table->maxlen != sizeof(u8)) err |= sysctl_err(path, table, "array not allowed"); + + if (table->extra1) { + extra = *(unsigned int *) table->extra1; + if (extra > 255U) + err |= sysctl_err(path, table, + "range value too large for proc_dou8vec_minmax"); + } + if (table->extra2) { + extra = *(unsigned int *) table->extra2; + if (extra > 255U) + err |= sysctl_err(path, table, + "range value too large for proc_dou8vec_minmax"); + } } if (table->proc_handler == proc_dobool) { diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c index 6ef887c19c48..4e7dcc9187e2 100644 --- a/kernel/sysctl-test.c +++ b/kernel/sysctl-test.c @@ -367,6 +367,54 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max( KUNIT_EXPECT_EQ(test, 0, *((int *)table.data)); } +/* + * Test that registering an invalid extra value is not allowed. + */ +static void sysctl_test_register_sysctl_sz_invalid_extra_value( + struct kunit *test) +{ + unsigned char data = 0; + struct ctl_table table_foo[] = { + { + .procname = "foo", + .data = &data, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_FOUR, + .extra2 = SYSCTL_ONE_THOUSAND, + }, + }; + + struct ctl_table table_bar[] = { + { + .procname = "bar", + .data = &data, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_NEG_ONE, + .extra2 = SYSCTL_ONE_HUNDRED, + }, + }; + + struct ctl_table table_qux[] = { + { + .procname = "qux", + .data = &data, + .maxlen = sizeof(u8), + .mode = 0644, + .proc_handler = proc_dou8vec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_TWO_HUNDRED, + }, + }; + + KUNIT_EXPECT_NULL(test, register_sysctl("foo", table_foo)); + KUNIT_EXPECT_NULL(test, register_sysctl("foo", table_bar)); + KUNIT_EXPECT_NOT_NULL(test, register_sysctl("foo", table_qux)); +} + static struct kunit_case sysctl_test_cases[] = { KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data), KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset), @@ -378,6 +426,7 @@ static struct kunit_case sysctl_test_cases[] = { KUNIT_CASE(sysctl_test_dointvec_write_happy_single_negative), KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min), KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max), + KUNIT_CASE(sysctl_test_register_sysctl_sz_invalid_extra_value), {} }; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index e0b917328cf9..c0a1164eaf59 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -977,16 +977,10 @@ int proc_dou8vec_minmax(struct ctl_table *table, int write, if (table->maxlen != sizeof(u8)) return -EINVAL; - if (table->extra1) { + if (table->extra1) min = *(unsigned int *) table->extra1; - if (min > 255U) - return -EINVAL; - } - if (table->extra2) { + if (table->extra2) max = *(unsigned int *) table->extra2; - if (max > 255U) - return -EINVAL; - } tmp = *table; -- cgit v1.2.3-58-ga151 From d7a76ec87195ced6910b0ca10ca133bb316c90f5 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 4 Jun 2024 08:29:21 +0200 Subject: sysctl: Remove check for sentinel element in ctl_table arrays Use ARRAY_SIZE exclusively by removing the check to ->procname in the stopping criteria of the loops traversing ctl_table arrays. This commit finalizes the removal of the sentinel elements at the end of ctl_table arrays which reduces the build time size and run time memory bloat by ~64 bytes per sentinel (further information Link : https://lore.kernel.org/all/ZO5Yx5JFogGi%2FcBo@bombadil.infradead.org/) Remove the entry->procname evaluation from the for loop stopping criteria in sysctl and sysctl_net. Signed-off-by: Joel Granados --- fs/proc/proc_sysctl.c | 2 +- net/sysctl_net.c | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index c467e36741d0..832de1c64b5a 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -21,7 +21,7 @@ #define list_for_each_table_entry(entry, header) \ entry = header->ctl_table; \ - for (size_t i = 0 ; i < header->ctl_table_size && entry->procname; ++i, entry++) + for (size_t i = 0 ; i < header->ctl_table_size; ++i, entry++) static const struct dentry_operations proc_sys_dentry_operations; static const struct file_operations proc_sys_file_operations; diff --git a/net/sysctl_net.c b/net/sysctl_net.c index f5017012a049..19e8048241ba 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -127,7 +127,7 @@ static void ensure_safe_net_sysctl(struct net *net, const char *path, pr_debug("Registering net sysctl (net %p): %s\n", net, path); ent = table; - for (size_t i = 0; i < table_size && ent->procname; ent++, i++) { + for (size_t i = 0; i < table_size; ent++, i++) { unsigned long addr; const char *where; @@ -165,17 +165,10 @@ struct ctl_table_header *register_net_sysctl_sz(struct net *net, struct ctl_table *table, size_t table_size) { - int count; - struct ctl_table *entry; - if (!net_eq(net, &init_net)) ensure_safe_net_sysctl(net, path, table, table_size); - entry = table; - for (count = 0 ; count < table_size && entry->procname; entry++, count++) - ; - - return __register_sysctl_table(&net->sysctls, path, table, count); + return __register_sysctl_table(&net->sysctls, path, table, table_size); } EXPORT_SYMBOL_GPL(register_net_sysctl_sz); -- cgit v1.2.3-58-ga151 From 55bb7eb62db4995cec7e309c2b9a1070cfd60eb6 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 4 Jun 2024 08:29:22 +0200 Subject: sysctl: Replace nr_entries with ctl_table_size in new_links The number of ctl_table entries (nr_entries) calculation was previously based on the ctl_table_size and the sentinel element. Since the sentinels have been removed, we remove the calculation and just use the ctl_table_size from the ctl_table_header. Signed-off-by: Joel Granados --- fs/proc/proc_sysctl.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 832de1c64b5a..dc95a7bde1b3 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1166,18 +1166,16 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_ struct ctl_table_header *links; struct ctl_node *node; char *link_name; - int nr_entries, name_bytes; + int name_bytes; name_bytes = 0; - nr_entries = 0; list_for_each_table_entry(entry, head) { - nr_entries++; name_bytes += strlen(entry->procname) + 1; } links = kzalloc(sizeof(struct ctl_table_header) + - sizeof(struct ctl_node)*nr_entries + - sizeof(struct ctl_table)*(nr_entries + 1) + + sizeof(struct ctl_node)*head->ctl_table_size + + sizeof(struct ctl_table)*(head->ctl_table_size + 1) + name_bytes, GFP_KERNEL); @@ -1185,8 +1183,8 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_ return NULL; node = (struct ctl_node *)(links + 1); - link_table = (struct ctl_table *)(node + nr_entries); - link_name = (char *)&link_table[nr_entries + 1]; + link_table = (struct ctl_table *)(node + head->ctl_table_size); + link_name = (char *)&link_table[head->ctl_table_size + 1]; link = link_table; list_for_each_table_entry(entry, head) { @@ -1200,7 +1198,7 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_ } init_header(links, dir->header.root, dir->header.set, node, link_table, head->ctl_table_size); - links->nreg = nr_entries; + links->nreg = head->ctl_table_size; return links; } -- cgit v1.2.3-58-ga151 From aef9d25e7f5631543a0276d0532151f2c61174d6 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 4 Jun 2024 08:29:23 +0200 Subject: sysctl: Remove superfluous empty allocations from sysctl internals Now that the sentinels have been removed from ctl_table arrays, there is no need to artificially append empty ctl_table elements at ctl_table registration. Remove superfluous empty allocation from new_dir and new_links. Signed-off-by: Joel Granados --- fs/proc/proc_sysctl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index dc95a7bde1b3..ee1c48f854a2 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -949,14 +949,14 @@ static struct ctl_dir *new_dir(struct ctl_table_set *set, char *new_name; new = kzalloc(sizeof(*new) + sizeof(struct ctl_node) + - sizeof(struct ctl_table)*2 + namelen + 1, + sizeof(struct ctl_table) + namelen + 1, GFP_KERNEL); if (!new) return NULL; node = (struct ctl_node *)(new + 1); table = (struct ctl_table *)(node + 1); - new_name = (char *)(table + 2); + new_name = (char *)(table + 1); memcpy(new_name, name, namelen); table[0].procname = new_name; table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO; @@ -1175,7 +1175,7 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_ links = kzalloc(sizeof(struct ctl_table_header) + sizeof(struct ctl_node)*head->ctl_table_size + - sizeof(struct ctl_table)*(head->ctl_table_size + 1) + + sizeof(struct ctl_table)*head->ctl_table_size + name_bytes, GFP_KERNEL); @@ -1184,7 +1184,7 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_ node = (struct ctl_node *)(links + 1); link_table = (struct ctl_table *)(node + head->ctl_table_size); - link_name = (char *)&link_table[head->ctl_table_size + 1]; + link_name = (char *)(link_table + head->ctl_table_size); link = link_table; list_for_each_table_entry(entry, head) { -- cgit v1.2.3-58-ga151 From a02fe70de4c2a7fbd971b49c5fa69f867fac16ff Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 4 Jun 2024 08:29:24 +0200 Subject: sysctl: Remove "child" sysctl code comments Erase the code comments mentioning "child" that were forgotten when the child element was removed in commit 2f2665c13af48 ("sysctl: replace child with an enumeration"). Signed-off-by: Joel Granados --- fs/proc/proc_sysctl.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index ee1c48f854a2..985c1774a552 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1310,28 +1310,23 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path) * __register_sysctl_table - register a leaf sysctl table * @set: Sysctl tree to register on * @path: The path to the directory the sysctl table is in. - * @table: the top-level table structure without any child. This table - * should not be free'd after registration. So it should not be - * used on stack. It can either be a global or dynamically allocated - * by the caller and free'd later after sysctl unregistration. + * + * @table: the top-level table structure. This table should not be free'd + * after registration. So it should not be used on stack. It can either + * be a global or dynamically allocated by the caller and free'd later + * after sysctl unregistration. * @table_size : The number of elements in table * * Register a sysctl table hierarchy. @table should be a filled in ctl_table * array. A completely 0 filled entry terminates the table. * * The members of the &struct ctl_table structure are used as follows: - * * procname - the name of the sysctl file under /proc/sys. Set to %NULL to not * enter a sysctl file - * - * data - a pointer to data for use by proc_handler - * - * maxlen - the maximum size in bytes of the data - * - * mode - the file permissions for the /proc/sys file - * - * child - must be %NULL. - * + * data - a pointer to data for use by proc_handler + * maxlen - the maximum size in bytes of the data + * mode - the file permissions for the /proc/sys file + * type - Defines the target type (described in struct definition) * proc_handler - the text handler routine (described below) * * extra1, extra2 - extra pointers usable by the proc handler routines @@ -1339,8 +1334,7 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path) * [0] https://lkml.kernel.org/87zgpte9o4.fsf@email.froward.int.ebiederm.org * * Leaf nodes in the sysctl tree will be represented by a single file - * under /proc; non-leaf nodes (where child is not NULL) are not allowed, - * sysctl_check_table() verifies this. + * under /proc; non-leaf nodes are not allowed. * * There must be a proc_handler routine for any terminal nodes. * Several default handlers are available to cover common cases - -- cgit v1.2.3-58-ga151 From 3717540377c500e28f4304b17a46dd5b025a61ac Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 4 Jun 2024 08:29:25 +0200 Subject: sysctl: Remove ctl_table sentinel code comments Remove the mention of a "zero terminated entry" from the __register_sysctl_table function doc. Signed-off-by: Joel Granados --- fs/proc/proc_sysctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 985c1774a552..87396f9d599d 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1318,7 +1318,7 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path) * @table_size : The number of elements in table * * Register a sysctl table hierarchy. @table should be a filled in ctl_table - * array. A completely 0 filled entry terminates the table. + * array. * * The members of the &struct ctl_table structure are used as follows: * procname - the name of the sysctl file under /proc/sys. Set to %NULL to not -- cgit v1.2.3-58-ga151 From acc154691fc75e1a178fc36624bdeee1420585a4 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 4 Jun 2024 08:29:26 +0200 Subject: sysctl: Warn on an empty procname element Add a pr_err warning in case a ctl_table is registered with a sentinel element containing a NULL procname. Signed-off-by: Joel Granados --- fs/proc/proc_sysctl.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 87396f9d599d..9553e77c9d31 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1131,6 +1131,8 @@ static int sysctl_check_table(const char *path, struct ctl_table_header *header) struct ctl_table *entry; int err = 0; list_for_each_table_entry(entry, header) { + if (!entry->procname) + err |= sysctl_err(path, entry, "procname is null"); if ((entry->proc_handler == proc_dostring) || (entry->proc_handler == proc_dobool) || (entry->proc_handler == proc_dointvec) || -- cgit v1.2.3-58-ga151