[PATCH v4 1/3] net: enforce net sysctl registration

From: Joel Granados

Date: Mon Aug 10 2026 - 09:19:36 EST


Replace the warning and file permission change with an error when an
"unsafe" net sysctl registration is detected.

One of the barriers preventing the const qualification of the ctl_tables
in the net directory is the permission (->mode) change in
ensure_safe_net_sysctl. This prep commit removes that barrier and
ensures that the received ctl_table pointer to the net ctl_table
register function is const.

Signed-off-by: Joel Granados <joel.granados@xxxxxxxxxx>
---
include/net/net_namespace.h | 5 +++--
net/sysctl_net.c | 25 +++++++++++++------------
2 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 501af1999fe8393d7c282a87645d1c4ceabadddc..e5ee673b9fcf846aefcc309d4cca1a8fc870aae2 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -525,12 +525,13 @@ struct ctl_table;
#ifdef CONFIG_SYSCTL
int net_sysctl_init(void);
struct ctl_table_header *register_net_sysctl_sz(struct net *net, const char *path,
- struct ctl_table *table, size_t table_size);
+ const struct ctl_table *table,
+ size_t table_size);
void unregister_net_sysctl_table(struct ctl_table_header *header);
#else
static inline int net_sysctl_init(void) { return 0; }
static inline struct ctl_table_header *register_net_sysctl_sz(struct net *net,
- const char *path, struct ctl_table *table, size_t table_size)
+ const char *path, const struct ctl_table *table, size_t table_size)
{
return NULL;
}
diff --git a/net/sysctl_net.c b/net/sysctl_net.c
index 19e8048241bacb18de853d3b904d0f97fd2fe78a..07f8434d4258c615c231ca7e29274de44b012665 100644
--- a/net/sysctl_net.c
+++ b/net/sysctl_net.c
@@ -114,16 +114,17 @@ __init int net_sysctl_init(void)
goto out;
}

-/* Verify that sysctls for non-init netns are safe by either:
+/* Return error when sysctls for non-init netns are unsafe by verifying:
* 1) being read-only, or
* 2) having a data pointer which points outside of the global kernel/module
* data segment, and rather into the heap where a per-net object was
* allocated.
*/
-static void ensure_safe_net_sysctl(struct net *net, const char *path,
- struct ctl_table *table, size_t table_size)
+static int ensure_safe_net_sysctl(struct net *net, const char *path,
+ const struct ctl_table *table,
+ size_t table_size)
{
- struct ctl_table *ent;
+ const struct ctl_table *ent;

pr_debug("Registering net sysctl (net %p): %s\n", net, path);
ent = table;
@@ -149,24 +150,24 @@ static void ensure_safe_net_sysctl(struct net *net, const char *path,
else
continue;

- /* If it is writable and points to kernel/module global
- * data, then it's probably a netns leak.
- */
+ /* Warn on netns leak. */
WARN(1, "sysctl %s/%s: data points to %s global data: %ps\n",
- path, ent->procname, where, ent->data);
+ path, ent->procname, where, ent->data);

- /* Make it "safe" by dropping writable perms */
- ent->mode &= ~0222;
+ return -EACCES;
}
+
+ return 0;
}

struct ctl_table_header *register_net_sysctl_sz(struct net *net,
const char *path,
- struct ctl_table *table,
+ const struct ctl_table *table,
size_t table_size)
{
if (!net_eq(net, &init_net))
- ensure_safe_net_sysctl(net, path, table, table_size);
+ if (ensure_safe_net_sysctl(net, path, table, table_size))
+ return NULL;

return __register_sysctl_table(&net->sysctls, path, table, table_size);
}

--
2.50.1