Netdev List
 help / color / mirror / Atom feed
* [PATCH RFC net-next v3 0/3] net: sysctl: Const Qualify sysctl ctl_table arrays
@ 2026-07-13 11:07 Joel Granados
  2026-07-13 11:07 ` [PATCH RFC net-next v3 1/3] net: enforce net sysctl registration Joel Granados
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Joel Granados @ 2026-07-13 11:07 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, David Ahern, Ido Schimmel, Pablo Neira Ayuso,
	Florian Westphal, Phil Sutter, Marcelo Ricardo Leitner, Xin Long,
	Steffen Klassert, Herbert Xu, D. Wythe, Dust Li, Sidraya Jayagond,
	Wenjia Zhang, Mahanta Jambigi, Tony Lu, Wen Gu, Kuniyuki Iwashima,
	Stefano Garzarella
  Cc: netdev, linux-kernel, netfilter-devel, coreteam, linux-sctp,
	linux-rdma, linux-s390, virtualization, Joel Granados

What?
=====
We do two things:
1. Reject netns-unsafe: Replace warning and file permission change with
   an error (reject registration) when an "unsafe" net sysctl
   registration is detected.
2. Const qualify: Const qualify network templated ctl_table arrays and
   unconditional kmemdup'ed ctl_table arrays.

Why?
====
The main motivation for this is to continue with the const qualification
of the ctl_table arrays [1]. The permission change inside
ensure_safe_net_sysctl disallows cons qualifiaction as it basically
modifies the entries before running the sysctl registration.

      ent->mode &= ~0222;

On reject netns-unsafe?
=======================
* I believe that there is currently now way that the permission change
  gets executed [2]
* I found one case where the warning message was posted to lore
  (vsock_sysctl_register) [3], but it made its to mainline as part of
  the second case in [2].
* We should error anyway because writing to the global sysctl value
  through a child netns is indicative of a bug [4].

On Const qualification?
=======================
We can separate the places where network registers sysctl tables into
three groups:
1. Static global: The unchanged global static arrays are passed along to
   sysctl register.
2. Always kmemdup: The global static arrays are always kmemdup'ed before
   passing them along to sysctl register.
3. Dynamic global: The global static array is changed in place before
   passing it along to sysctl register.

This series handles case 1 and 2. It leaves 3 for a later point as
const qualifying those global ctl_tables is more involved.

RFC
===
Keeping the RFC tag for now in hope of any preliminary feedback. I would
be very thankful if you point me to anything that I have missed in my
analysis that shows that this cannot/shouldn't be done.

Changes in v3:
- Const qualified 2 of the 3 cases within the net directory ctl_table
  register sites.
- Link to v2: https://lore.kernel.org/r/20260707-jag-net_const_qualify-v2-1-5a5c52031ead@kernel.org

Changes in v2:
- Rebased on top of net-next
- Updated subject to "RFC net-next"
- Link to v1: https://lore.kernel.org/r/20260629-jag-net_const_qualify-v1-1-ee98b8fc400c@kernel.org

Best

[1]
  https://git.kernel.org/pub/scm/linux/kernel/git/sysctl/sysctl.git/commit/?h=constfy-sysctl-6.14-rc1&id=1751f872cc97f992ed5c4c72c55588db1f0021e1

[2]
  I have identified 4 contexts relevant to the ensure_safe_net_sysctl call
  inside the network sysctl registration.

  1. When the (struct net) == &init_net (like in iw_cm_init): In this case
     ensure_safe_net_sysctl is not executed and permission modification
     never happens.

  2. When the ctl_table data (->data) gets "manually" assigned to
     something other init_net (like in vsock_sysctl_register): In this
     case ensure_safe_net_sysctl *is* executed but the data that is passed
     is neither a module address (!is_module_address) nor a kernel core
     address (!is_kernel_core_data); so the permission modification never
     happens.

  3. When the permissions are explicitly changed on a kmemdup'ed ctl_table
     array (like in sysctl_core_net_init): in this case
     ensure_safe_net_sysctl *is* executed but the permission modification
     never happens as the mode is not writable.

  4. When ctl have custom proc_handlers (like in nf_lwtunnel_net_init): In
     this case ->data is NULL so it is not a module address
     (!is_module_address) nor a kernel core address
     (!is_kernel_core_data), so permission modification never happens.

  It seems like there is no way of executing the permission change in
  ensure_safe_net_sysctl. Please correct me if this is inaccurate and help
  me find the case that I missed.

[3]
  https://lore.kernel.org/all/20260302194926.90378-1-graf@amazon.com/

[4]
  The ensure_safe_net_sysctl function was introduced in Commit:
  31c4d2f160eb7b17cbead24dc6efed06505a3fee ("net: Ensure net namespace
  isolation of sysctls") which states that it is trying to prevent a
  leak (indicative of a bug).

---
Signed-off-by: Joel Granados <joel.granados@kernel.org>

---
Joel Granados (3):
      net: enforce net sysctl registration
      net: Const qualify ctl_tables that kmemdup unconditionally
      net: Const qualify network templated ctl_tables Arrays

 include/net/net_namespace.h             |  4 +--
 net/core/sysctl_net_core.c              | 38 +++++++++++++++--------
 net/ipv4/devinet.c                      |  2 +-
 net/ipv4/sysctl_net_ipv4.c              | 54 +++++++++++++++++++--------------
 net/ipv4/xfrm4_policy.c                 | 22 +++++++++++---
 net/ipv6/icmp.c                         |  2 +-
 net/ipv6/route.c                        |  2 +-
 net/ipv6/sysctl_net_ipv6.c              |  2 +-
 net/ipv6/xfrm6_policy.c                 | 22 +++++++++++---
 net/netfilter/nf_conntrack_standalone.c |  2 +-
 net/netfilter/nf_hooks_lwtunnel.c       |  4 +--
 net/sctp/sysctl.c                       |  2 +-
 net/smc/smc_sysctl.c                    | 26 +++++++++++-----
 net/sysctl_net.c                        | 24 +++++++--------
 net/unix/sysctl_net_unix.c              | 21 ++++++++++---
 net/vmw_vsock/af_vsock.c                | 25 ++++++++++-----
 net/xfrm/xfrm_sysctl.c                  |  2 +-
 17 files changed, 167 insertions(+), 87 deletions(-)
---
base-commit: 474cff6868129755cf889edf40d7f491729fc588
change-id: 20260629-jag-net_const_qualify-f4e09759dac7

Best regards,
-- 
Joel Granados <joel.granados@kernel.org>



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH RFC net-next v3 1/3] net: enforce net sysctl registration
  2026-07-13 11:07 [PATCH RFC net-next v3 0/3] net: sysctl: Const Qualify sysctl ctl_table arrays Joel Granados
@ 2026-07-13 11:07 ` Joel Granados
  2026-07-13 11:07 ` [PATCH RFC net-next v3 2/3] net: Const qualify ctl_tables that kmemdup unconditionally Joel Granados
  2026-07-13 11:07 ` [PATCH RFC net-next v3 3/3] net: Const qualify network templated ctl_tables Arrays Joel Granados
  2 siblings, 0 replies; 4+ messages in thread
From: Joel Granados @ 2026-07-13 11:07 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, David Ahern, Ido Schimmel, Pablo Neira Ayuso,
	Florian Westphal, Phil Sutter, Marcelo Ricardo Leitner, Xin Long,
	Steffen Klassert, Herbert Xu, D. Wythe, Dust Li, Sidraya Jayagond,
	Wenjia Zhang, Mahanta Jambigi, Tony Lu, Wen Gu, Kuniyuki Iwashima,
	Stefano Garzarella
  Cc: netdev, linux-kernel, netfilter-devel, coreteam, linux-sctp,
	linux-rdma, linux-s390, virtualization, Joel Granados

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@kernel.org>
---
 include/net/net_namespace.h |  4 ++--
 net/sysctl_net.c            | 24 ++++++++++++------------
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index 80de5e98a66d6c9273aa7c5b9d489b22cef8559a..dca0ec809483bec604f4ca3d99dfea32834af8fa 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -522,12 +522,12 @@ 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..4714887113d90a191c300c9c49a6317d5609efeb 100644
--- a/net/sysctl_net.c
+++ b/net/sysctl_net.c
@@ -114,16 +114,16 @@ __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 +149,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



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH RFC net-next v3 2/3] net: Const qualify ctl_tables that kmemdup unconditionally
  2026-07-13 11:07 [PATCH RFC net-next v3 0/3] net: sysctl: Const Qualify sysctl ctl_table arrays Joel Granados
  2026-07-13 11:07 ` [PATCH RFC net-next v3 1/3] net: enforce net sysctl registration Joel Granados
@ 2026-07-13 11:07 ` Joel Granados
  2026-07-13 11:07 ` [PATCH RFC net-next v3 3/3] net: Const qualify network templated ctl_tables Arrays Joel Granados
  2 siblings, 0 replies; 4+ messages in thread
From: Joel Granados @ 2026-07-13 11:07 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, David Ahern, Ido Schimmel, Pablo Neira Ayuso,
	Florian Westphal, Phil Sutter, Marcelo Ricardo Leitner, Xin Long,
	Steffen Klassert, Herbert Xu, D. Wythe, Dust Li, Sidraya Jayagond,
	Wenjia Zhang, Mahanta Jambigi, Tony Lu, Wen Gu, Kuniyuki Iwashima,
	Stefano Garzarella
  Cc: netdev, linux-kernel, netfilter-devel, coreteam, linux-sctp,
	linux-rdma, linux-s390, virtualization, Joel Granados

Const qualify clt_table arrays in the net directory that always pass a
memory duplicate to sysctl register. The template would then be in
.rodata and the kmemdup'ed array would be outside.

Signed-off-by: Joel Granados <joel.granados@kernel.org>
---
 net/ipv4/devinet.c                      | 2 +-
 net/ipv6/icmp.c                         | 2 +-
 net/ipv6/route.c                        | 2 +-
 net/ipv6/sysctl_net_ipv6.c              | 2 +-
 net/netfilter/nf_conntrack_standalone.c | 2 +-
 net/sctp/sysctl.c                       | 2 +-
 net/xfrm/xfrm_sysctl.c                  | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index a35b72662e431661da1672f428cae6bb3110480b..19edc08ae20c4f16d3bcf479dc25022d55cbb5af 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -2798,7 +2798,7 @@ static void devinet_sysctl_unregister(struct in_device *idev)
 	neigh_sysctl_unregister(idev->arp_parms);
 }
 
-static struct ctl_table ctl_forward_entry[] = {
+static const struct ctl_table ctl_forward_entry[] = {
 	{
 		.procname	= "ip_forward",
 		.data		= &ipv4_devconf.data[
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index efb23807a0262e8d68aa1afc8d96ee94eab89d50..a95b0351824f3237815e43bf8448110070955884 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -1374,7 +1374,7 @@ EXPORT_SYMBOL(icmpv6_err_convert);
 static u32 icmpv6_errors_extension_mask_all =
 	GENMASK_U8(ICMP_ERR_EXT_COUNT - 1, 0);
 
-static struct ctl_table ipv6_icmp_table_template[] = {
+static const struct ctl_table ipv6_icmp_table_template[] = {
 	{
 		.procname	= "ratelimit",
 		.data		= &init_net.ipv6.sysctl.icmpv6_time,
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index a1301334da48c0f911da06ce448a76ecfb0d25cf..96b37c102a634c6715a5fbd1d39ca415302ff859 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -6555,7 +6555,7 @@ static int ipv6_sysctl_rtcache_flush(const struct ctl_table *ctl, int write,
 	return 0;
 }
 
-static struct ctl_table ipv6_route_table_template[] = {
+static const struct ctl_table ipv6_route_table_template[] = {
 	{
 		.procname	=	"max_size",
 		.data		=	&init_net.ipv6.sysctl.ip6_rt_max_size,
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index d2cd33e2698d5c88df4718c9622dba2d574fa309..1a0a36dcdabc1be961d0ab69e5c93b05c53f46a8 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -61,7 +61,7 @@ proc_rt6_multipath_hash_fields(const struct ctl_table *table, int write, void *b
 	return ret;
 }
 
-static struct ctl_table ipv6_table_template[] = {
+static const struct ctl_table ipv6_table_template[] = {
 	{
 		.procname	= "bindv6only",
 		.data		= &init_net.ipv6.sysctl.bindv6only,
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index be2953c7d702e92031d4bcf7e707741abed0f49c..f4f2d82192d54ed9831b9677743f1139820e5a2e 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -639,7 +639,7 @@ enum nf_ct_sysctl_index {
 	NF_SYSCTL_CT_LAST_SYSCTL,
 };
 
-static struct ctl_table nf_ct_sysctl_table[] = {
+static const struct ctl_table nf_ct_sysctl_table[] = {
 	[NF_SYSCTL_CT_MAX] = {
 		.procname	= "nf_conntrack_max",
 		.data		= &nf_conntrack_max,
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 15e7db9a3ab2e325f3951ac20c067a973a049618..331f45af9c4990d78a10a5c2c4efbcbca21813dc 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -92,7 +92,7 @@ static struct ctl_table sctp_table[] = {
 #define SCTP_PF_RETRANS_IDX    2
 #define SCTP_PS_RETRANS_IDX    3
 
-static struct ctl_table sctp_net_table[] = {
+static const struct ctl_table sctp_net_table[] = {
 	[SCTP_RTO_MIN_IDX] = {
 		.procname	= "rto_min",
 		.data		= &init_net.sctp.rto_min,
diff --git a/net/xfrm/xfrm_sysctl.c b/net/xfrm/xfrm_sysctl.c
index ca003e8a03760cd8dbb9e9f7cd5a9738eeeb7e71..357152a50faf10e5c33468c034dd1777e0bed079 100644
--- a/net/xfrm/xfrm_sysctl.c
+++ b/net/xfrm/xfrm_sysctl.c
@@ -13,7 +13,7 @@ static void __net_init __xfrm_sysctl_init(struct net *net)
 }
 
 #ifdef CONFIG_SYSCTL
-static struct ctl_table xfrm_table[] = {
+static const struct ctl_table xfrm_table[] = {
 	{
 		.procname	= "xfrm_aevent_etime",
 		.maxlen		= sizeof(u32),

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH RFC net-next v3 3/3] net: Const qualify network templated ctl_tables Arrays
  2026-07-13 11:07 [PATCH RFC net-next v3 0/3] net: sysctl: Const Qualify sysctl ctl_table arrays Joel Granados
  2026-07-13 11:07 ` [PATCH RFC net-next v3 1/3] net: enforce net sysctl registration Joel Granados
  2026-07-13 11:07 ` [PATCH RFC net-next v3 2/3] net: Const qualify ctl_tables that kmemdup unconditionally Joel Granados
@ 2026-07-13 11:07 ` Joel Granados
  2 siblings, 0 replies; 4+ messages in thread
From: Joel Granados @ 2026-07-13 11:07 UTC (permalink / raw)
  To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, David Ahern, Ido Schimmel, Pablo Neira Ayuso,
	Florian Westphal, Phil Sutter, Marcelo Ricardo Leitner, Xin Long,
	Steffen Klassert, Herbert Xu, D. Wythe, Dust Li, Sidraya Jayagond,
	Wenjia Zhang, Mahanta Jambigi, Tony Lu, Wen Gu, Kuniyuki Iwashima,
	Stefano Garzarella
  Cc: netdev, linux-kernel, netfilter-devel, coreteam, linux-sctp,
	linux-rdma, linux-s390, virtualization, Joel Granados

Add duplication helpers in the cases where the ctl_table array elements
are modified after duplication. Helpers return a ctl_table as const
pointer allowing the const qualification of the static global ctl_table
array.

Signed-off-by: Joel Granados <joel.granados@kernel.org>
---
 net/core/sysctl_net_core.c        | 38 +++++++++++++++++----------
 net/ipv4/sysctl_net_ipv4.c        | 54 +++++++++++++++++++++++----------------
 net/ipv4/xfrm4_policy.c           | 22 ++++++++++++----
 net/ipv6/xfrm6_policy.c           | 22 ++++++++++++----
 net/netfilter/nf_hooks_lwtunnel.c |  4 +--
 net/smc/smc_sysctl.c              | 26 ++++++++++++++-----
 net/unix/sysctl_net_unix.c        | 21 +++++++++++----
 net/vmw_vsock/af_vsock.c          | 25 +++++++++++++-----
 8 files changed, 146 insertions(+), 66 deletions(-)

diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index b508618bfc12393ba926ebf5a2dd4ea73ef03ee8..eb35da3556f4aa00cecd4582ab94e339d2518506 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -678,7 +678,7 @@ static struct ctl_table net_core_table[] = {
 	},
 };
 
-static struct ctl_table netns_core_table[] = {
+static const struct ctl_table netns_core_table[] = {
 #if IS_ENABLED(CONFIG_RPS)
 	{
 		.procname	= "rps_default_mask",
@@ -787,26 +787,38 @@ static int __init fb_tunnels_only_for_init_net_sysctl_setup(char *str)
 }
 __setup("fb_tunnels=", fb_tunnels_only_for_init_net_sysctl_setup);
 
-static __net_init int sysctl_core_net_init(struct net *net)
+static const struct ctl_table *netns_core_table_dup(struct net *net)
 {
 	size_t table_size = ARRAY_SIZE(netns_core_table);
 	struct ctl_table *tbl;
+	int i;
+
+	tbl = kmemdup(netns_core_table, sizeof(netns_core_table), GFP_KERNEL);
+	if (!tbl)
+		return NULL;
+
+	for (i = 0; i < table_size; ++i) {
+		if (tbl[i].data == &sysctl_wmem_max)
+			break;
+
+		tbl[i].data += (char *)net - (char *)&init_net;
+	}
+	for (; i < table_size; ++i)
+		tbl[i].mode &= ~0222;
+
+	return tbl;
+}
+
+static __net_init int sysctl_core_net_init(struct net *net)
+{
+	size_t table_size = ARRAY_SIZE(netns_core_table);
+	const struct ctl_table *tbl;
 
 	tbl = netns_core_table;
 	if (!net_eq(net, &init_net)) {
-		int i;
-		tbl = kmemdup(tbl, sizeof(netns_core_table), GFP_KERNEL);
+		tbl = netns_core_table_dup(net);
 		if (tbl == NULL)
 			goto err_dup;
-
-		for (i = 0; i < table_size; ++i) {
-			if (tbl[i].data == &sysctl_wmem_max)
-				break;
-
-			tbl[i].data += (char *)net - (char *)&init_net;
-		}
-		for (; i < table_size; ++i)
-			tbl[i].mode &= ~0222;
 	}
 
 	net->core.sysctl_hdr = register_net_sysctl_sz(net, "net/core", tbl, table_size);
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index ca1180dba1dea9ce72028ba49b7f953da343336b..2f0363bca2a88d68276670cfce6fb04398f82bc5 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -624,7 +624,7 @@ static struct ctl_table ipv4_table[] = {
 	},
 };
 
-static struct ctl_table ipv4_net_table[] = {
+static const struct ctl_table ipv4_net_table[] = {
 	{
 		.procname	= "tcp_max_tw_buckets",
 		.data		= &init_net.ipv4.tcp_death_row.sysctl_max_tw_buckets,
@@ -1654,35 +1654,45 @@ static struct ctl_table ipv4_net_table[] = {
 	},
 };
 
-static __net_init int ipv4_sysctl_init_net(struct net *net)
+static const struct ctl_table *ipv4_net_table_dup(struct net *net)
 {
 	size_t table_size = ARRAY_SIZE(ipv4_net_table);
 	struct ctl_table *table;
+	int i;
+
+	table = kmemdup(ipv4_net_table, sizeof(ipv4_net_table), GFP_KERNEL);
+	if (!table)
+		return NULL;
+
+	for (i = 0; i < table_size; i++) {
+		if (table[i].data) {
+			/* Update the variables to point into
+			 * the current struct net
+			 */
+			table[i].data += (void *)net - (void *)&init_net;
+		} else {
+			/* Entries without data pointer are global;
+			 * Make them read-only in non-init_net ns
+			 */
+			table[i].mode &= ~0222;
+		}
+		if (table[i].extra2 >= (void *)&init_net.ipv4 &&
+		    table[i].extra2 < (void *)(&init_net.ipv4 + 1))
+			table[i].extra2 += (void *)net - (void *)&init_net;
+	}
+	return table;
+}
+
+static __net_init int ipv4_sysctl_init_net(struct net *net)
+{
+	size_t table_size = ARRAY_SIZE(ipv4_net_table);
+	const struct ctl_table *table;
 
 	table = ipv4_net_table;
 	if (!net_eq(net, &init_net)) {
-		int i;
-
-		table = kmemdup(table, sizeof(ipv4_net_table), GFP_KERNEL);
+		table = ipv4_net_table_dup(net);
 		if (!table)
 			goto err_alloc;
-
-		for (i = 0; i < table_size; i++) {
-			if (table[i].data) {
-				/* Update the variables to point into
-				 * the current struct net
-				 */
-				table[i].data += (void *)net - (void *)&init_net;
-			} else {
-				/* Entries without data pointer are global;
-				 * Make them read-only in non-init_net ns
-				 */
-				table[i].mode &= ~0222;
-			}
-			if (table[i].extra2 >= (void *)&init_net.ipv4 &&
-			    table[i].extra2 < (void *)(&init_net.ipv4 + 1))
-				table[i].extra2 += (void *)net - (void *)&init_net;
-		}
 	}
 
 	net->ipv4.ipv4_hdr = register_net_sysctl_sz(net, "net/ipv4", table,
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 58faf1ddd2b151e4569bb6351029718dac37521b..ab7a01029d490416d36482f7a3189f83d6670f42 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -141,7 +141,7 @@ static const struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
 };
 
 #ifdef CONFIG_SYSCTL
-static struct ctl_table xfrm4_policy_table[] = {
+static const struct ctl_table xfrm4_policy_table[] = {
 	{
 		.procname       = "xfrm4_gc_thresh",
 		.data           = &init_net.xfrm.xfrm4_dst_ops.gc_thresh,
@@ -151,18 +151,30 @@ static struct ctl_table xfrm4_policy_table[] = {
 	},
 };
 
-static __net_init int xfrm4_net_sysctl_init(struct net *net)
+static const struct ctl_table *xfrm4_policy_table_dup(struct net *net)
 {
 	struct ctl_table *table;
+
+	table = kmemdup(xfrm4_policy_table, sizeof(xfrm4_policy_table),
+			GFP_KERNEL);
+	if (!table)
+		return NULL;
+
+	table[0].data = &net->xfrm.xfrm4_dst_ops.gc_thresh;
+
+	return table;
+}
+
+static __net_init int xfrm4_net_sysctl_init(struct net *net)
+{
+	const struct ctl_table *table;
 	struct ctl_table_header *hdr;
 
 	table = xfrm4_policy_table;
 	if (!net_eq(net, &init_net)) {
-		table = kmemdup(table, sizeof(xfrm4_policy_table), GFP_KERNEL);
+		table = xfrm4_policy_table_dup(net);
 		if (!table)
 			goto err_alloc;
-
-		table[0].data = &net->xfrm.xfrm4_dst_ops.gc_thresh;
 	}
 
 	hdr = register_net_sysctl_sz(net, "net/ipv4", table,
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 125ea9a5b8a082052380b7fd7ed7123f5247d7cc..1e0385b62cde3f6d23382f92bbad5d7fdd09f1ef 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -186,7 +186,7 @@ static void xfrm6_policy_fini(void)
 }
 
 #ifdef CONFIG_SYSCTL
-static struct ctl_table xfrm6_policy_table[] = {
+static const struct ctl_table xfrm6_policy_table[] = {
 	{
 		.procname       = "xfrm6_gc_thresh",
 		.data		= &init_net.xfrm.xfrm6_dst_ops.gc_thresh,
@@ -196,18 +196,30 @@ static struct ctl_table xfrm6_policy_table[] = {
 	},
 };
 
-static int __net_init xfrm6_net_sysctl_init(struct net *net)
+static const struct ctl_table *xfrm6_policy_table_dup(struct net *net)
 {
 	struct ctl_table *table;
+
+	table = kmemdup(xfrm6_policy_table, sizeof(xfrm6_policy_table),
+			GFP_KERNEL);
+	if (!table)
+		return NULL;
+
+	table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
+
+	return table;
+}
+
+static int __net_init xfrm6_net_sysctl_init(struct net *net)
+{
+	const struct ctl_table *table;
 	struct ctl_table_header *hdr;
 
 	table = xfrm6_policy_table;
 	if (!net_eq(net, &init_net)) {
-		table = kmemdup(table, sizeof(xfrm6_policy_table), GFP_KERNEL);
+		table = xfrm6_policy_table_dup(net);
 		if (!table)
 			goto err_alloc;
-
-		table[0].data = &net->xfrm.xfrm6_dst_ops.gc_thresh;
 	}
 
 	hdr = register_net_sysctl_sz(net, "net/ipv6", table,
diff --git a/net/netfilter/nf_hooks_lwtunnel.c b/net/netfilter/nf_hooks_lwtunnel.c
index 2d890dd04ff89041e6aec3741f24cdd7bc47d1fe..4e1eef1ba0f1559ca35f024723af551c6c9e7d35 100644
--- a/net/netfilter/nf_hooks_lwtunnel.c
+++ b/net/netfilter/nf_hooks_lwtunnel.c
@@ -54,7 +54,7 @@ int nf_hooks_lwtunnel_sysctl_handler(const struct ctl_table *table, int write,
 }
 EXPORT_SYMBOL_GPL(nf_hooks_lwtunnel_sysctl_handler);
 
-static struct ctl_table nf_lwtunnel_sysctl_table[] = {
+static const struct ctl_table nf_lwtunnel_sysctl_table[] = {
 	{
 		.procname	= "nf_hooks_lwtunnel",
 		.data		= NULL,
@@ -66,8 +66,8 @@ static struct ctl_table nf_lwtunnel_sysctl_table[] = {
 
 static int __net_init nf_lwtunnel_net_init(struct net *net)
 {
+	const struct ctl_table *table;
 	struct ctl_table_header *hdr;
-	struct ctl_table *table;
 
 	table = nf_lwtunnel_sysctl_table;
 	if (!net_eq(net, &init_net)) {
diff --git a/net/smc/smc_sysctl.c b/net/smc/smc_sysctl.c
index b1efed5462435b1a6f2f59584a4cf47f5f6e1981..09dad48337f6164f5765fa793412bdebf47e61ca 100644
--- a/net/smc/smc_sysctl.c
+++ b/net/smc/smc_sysctl.c
@@ -97,7 +97,7 @@ static int proc_smc_hs_ctrl(const struct ctl_table *ctl, int write,
 }
 #endif /* CONFIG_SMC_HS_CTRL_BPF */
 
-static struct ctl_table smc_table[] = {
+static const struct ctl_table smc_table[] = {
 	{
 		.procname       = "autocorking_size",
 		.data           = &init_net.smc.sysctl_autocorking_size,
@@ -195,14 +195,29 @@ static struct ctl_table smc_table[] = {
 #endif /* CONFIG_SMC_HS_CTRL_BPF */
 };
 
-int __net_init smc_sysctl_net_init(struct net *net)
+static const struct ctl_table *smc_table_dup(struct net *net)
 {
 	size_t table_size = ARRAY_SIZE(smc_table);
 	struct ctl_table *table;
+	int i;
+
+	table = kmemdup(smc_table, sizeof(smc_table), GFP_KERNEL);
+	if (!table)
+		return NULL;
+
+	for (i = 0; i < table_size; i++)
+		table[i].data += (void *)net - (void *)&init_net;
+
+	return table;
+}
+
+int __net_init smc_sysctl_net_init(struct net *net)
+{
+	size_t table_size = ARRAY_SIZE(smc_table);
+	const struct ctl_table *table;
 
 	table = smc_table;
 	if (!net_eq(net, &init_net)) {
-		int i;
 #if IS_ENABLED(CONFIG_SMC_HS_CTRL_BPF)
 		struct smc_hs_ctrl *ctrl;
 
@@ -214,12 +229,9 @@ int __net_init smc_sysctl_net_init(struct net *net)
 		rcu_read_unlock();
 #endif /* CONFIG_SMC_HS_CTRL_BPF */
 
-		table = kmemdup(table, sizeof(smc_table), GFP_KERNEL);
+		table = smc_table_dup(net);
 		if (!table)
 			goto err_alloc;
-
-		for (i = 0; i < table_size; i++)
-			table[i].data += (void *)net - (void *)&init_net;
 	}
 
 	net->smc.smc_hdr = register_net_sysctl_sz(net, "net/smc", table,
diff --git a/net/unix/sysctl_net_unix.c b/net/unix/sysctl_net_unix.c
index e02ed6e3955c06b60cf4afb02656df8956f075ba..47660d5726bbd7d812762f4feffa9a0a42499d7d 100644
--- a/net/unix/sysctl_net_unix.c
+++ b/net/unix/sysctl_net_unix.c
@@ -13,7 +13,7 @@
 
 #include "af_unix.h"
 
-static struct ctl_table unix_table[] = {
+static const struct ctl_table unix_table[] = {
 	{
 		.procname	= "max_dgram_qlen",
 		.data		= &init_net.unx.sysctl_max_dgram_qlen,
@@ -23,18 +23,29 @@ static struct ctl_table unix_table[] = {
 	},
 };
 
-int __net_init unix_sysctl_register(struct net *net)
+static const struct ctl_table *unix_table_dup(struct net *net)
 {
 	struct ctl_table *table;
 
+	table = kmemdup(unix_table, sizeof(unix_table), GFP_KERNEL);
+	if (!table)
+		return NULL;
+
+	table[0].data = &net->unx.sysctl_max_dgram_qlen;
+
+	return table;
+}
+
+int __net_init unix_sysctl_register(struct net *net)
+{
+	const struct ctl_table *table;
+
 	if (net_eq(net, &init_net)) {
 		table = unix_table;
 	} else {
-		table = kmemdup(unix_table, sizeof(unix_table), GFP_KERNEL);
+		table = unix_table_dup(net);
 		if (!table)
 			goto err_alloc;
-
-		table[0].data = &net->unx.sysctl_max_dgram_qlen;
 	}
 
 	net->unx.ctl = register_net_sysctl_sz(net, "net/unix", table,
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 622dbd0467994428f1a590f559b78d8c17f6ba60..caebef73ea58d2b6043ca3fe3b6872f92fbe9fa6 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -2899,7 +2899,7 @@ static int vsock_net_child_mode_string(const struct ctl_table *table, int write,
 	return 0;
 }
 
-static struct ctl_table vsock_table[] = {
+static const struct ctl_table vsock_table[] = {
 	{
 		.procname	= "ns_mode",
 		.data		= &init_net.vsock.mode,
@@ -2925,20 +2925,31 @@ static struct ctl_table vsock_table[] = {
 	},
 };
 
-static int __net_init vsock_sysctl_register(struct net *net)
+static const struct ctl_table *vsock_table_dup(struct net *net)
 {
 	struct ctl_table *table;
 
+	table = kmemdup(vsock_table, sizeof(vsock_table), GFP_KERNEL);
+	if (!table)
+		return NULL;
+
+	table[0].data = &net->vsock.mode;
+	table[1].data = &net->vsock.child_ns_mode;
+	table[2].data = &net->vsock.g2h_fallback;
+
+	return table;
+}
+
+static int __net_init vsock_sysctl_register(struct net *net)
+{
+	const struct ctl_table *table;
+
 	if (net_eq(net, &init_net)) {
 		table = vsock_table;
 	} else {
-		table = kmemdup(vsock_table, sizeof(vsock_table), GFP_KERNEL);
+		table = vsock_table_dup(net);
 		if (!table)
 			goto err_alloc;
-
-		table[0].data = &net->vsock.mode;
-		table[1].data = &net->vsock.child_ns_mode;
-		table[2].data = &net->vsock.g2h_fallback;
 	}
 
 	net->vsock.sysctl_hdr = register_net_sysctl_sz(net, "net/vsock", table,

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-07-13 11:07 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 11:07 [PATCH RFC net-next v3 0/3] net: sysctl: Const Qualify sysctl ctl_table arrays Joel Granados
2026-07-13 11:07 ` [PATCH RFC net-next v3 1/3] net: enforce net sysctl registration Joel Granados
2026-07-13 11:07 ` [PATCH RFC net-next v3 2/3] net: Const qualify ctl_tables that kmemdup unconditionally Joel Granados
2026-07-13 11:07 ` [PATCH RFC net-next v3 3/3] net: Const qualify network templated ctl_tables Arrays Joel Granados

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox