mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context
@ 2026-10-28 16:34 wen.yang
  2026-10-28 16:34 ` [RFC PATCH 01/39] " wen.yang
                   ` (38 more replies)
  0 siblings, 39 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

struct ctl_table describes an entry with data, maxlen and, until this
series, two untyped misc pointers in extra1 and extra2. Those two pointers
have accumulated at least seven unrelated jobs: a fixed bound, a bound read
through a pointer on every call, an object or namespace pointer, a small
integer cookie encoded into the pointer, a lock signal, NULL standing for
"no bound", and a value the core itself casts to unsigned int while
validating a u8 range. Nothing in the entry says which job applies, so
readers infer it from the handler, and the core gets it wrong where it
treats extra1 as an unsigned int.

The numeric half of this change is the direction Eric Biederman asked for:

  You can still save a lot more by turning .extra1 and .extra2
  into longs instead of keeping them as pointers and needing
  constants to be pointed at somewhere.

  [...] I don't see any reason why you want shared
  constants for such a handful of things.  Especially when
  it has proven to be error prone in the past.

min/max are plain unsigned long members, so the comparisons are ordinary
C and cannot be confused with a pointer. The shared const arrays that only
existed to give those pointers an address (sysctl_vals and
sysctl_long_vals) are removed as well.

The tree already records this direction. fs/proc/proc_sysctl.c documents
extra1/extra2 as "extra pointers usable by the proc handler routines" and
carries:

  XXX: we should eventually modify these to use long min / max [0]
  [0] https://lkml.kernel.org/87zgpte9o4.fsf@email.froward.int.ebiederm.org

This series completes that TODO and removes the comment.

priv is this series' addition: the same pair of fields also hides handler
context, which a numeric bound cannot express, and keeping it out of data
is what removes the restore-before-delegate pattern.

The overloading is also a memory-safety hazard. The obvious alternative --
keep handler context in data and restore the value pointer in each handler
-- needs an original_data field in nine addrconf and two devinet handlers;
one missed restore turns a user-supplied integer into a write over a kernel
pointer. nf_log reaches the same problem from the other side: it replaces
data with a local buffer inside a single handler, so data means two things
within one function. This revision removes that temptation instead of
managing it.

The core is not exempt. sysctl_check_table_array() reads
*(unsigned int *)table->extra1 to validate a u8 range. That is only correct
while every u8 entry happens to point extra1/extra2 at an int or unsigned
int; a long bound, a context pointer or an encoded integer would be read as
four bytes of the wrong object. No such entry exists today, so this is a
latent class rather than a live bug, but the invariant is untyped and
unchecked, and nf_log already encodes an integer into extra1.

The refactor is the first five patches; they define the replacement
contract and prove it covers every entry shape the tree uses today.

  1. min/max hold numeric bounds by value, and priv holds handler context.
     The range converters read min/max whenever extra1 and extra2 are
     clear and keep reading the pointers while they are set, so existing
     callers keep working while they migrate and no parallel handler API or
     renamed symbol is introduced. Signed handlers read the bounds as
     signed longs, an absent endpoint is the type limit rather than NULL so
     zero stays a real bound, and a run-time range is refreshed before
     every call. Registration rejects priv on a standard handler.

  2. The debug test module is converted to numeric bounds as the first
     user.

  3. KUnit coverage for numeric bounds and the handlers that consume them:
     full type ranges, one-sided and zero bounds, entries without backing
     storage, explicit handlers for arrays and members, and rejection of
     writes that would partially commit.

  4. KUnit coverage for run-time ranges across the scalar widths and
     jiffies, run-time bound pointers, sibling bounds, typed context, owner
     derivation, bool, registration lifecycles, and simulated per-net
     duplication.

  5. KUnit coverage for priv: a standard handler must ignore it,
     registration must reject it with a standard handler, and
     re-registration must keep both the value pointer and the context. The
     same patch moves the debug module's dynamic-value entry to the
     data/priv shape.

On the CTLTBL_ENTRY_* constructors: I kept the naming and argument order you
suggested while reworking the series. As it turned out, no production table
needed them -- the converted entries read naturally as designated
initializers -- so I have not introduced them as a public API in this
version; the KUnit file keeps an equivalent constructor privately, so the
tests stay readable. I still think the public version is worth doing, and I
would be happy to send it as a follow-up once there are real users, so the
API and its callers can be reviewed together.

Everything after patch 5 is migration and adds no new contract. Patches
6-32 convert the remaining users and derive owners from their backing
storage. Patches 33-38 move handler context onto priv, one subsystem per
patch; each of those files is touched exactly once and goes straight from
the old pointer field to priv, with no intermediate data-as-context step.
Patch 39 removes the extra pointers and the compatibility read.

The parport conversion illustrates a concrete problem with the overloaded
fields. In the base tree, registration overwrites the spintime minimum
pointer in vars[0].extra1 with a port pointer. The range handler can then
check a write against data from struct parport instead of the intended
minimum. Moving the port context to priv and using numeric bounds also
corrects this existing bug. A separate fix can address it independently;
this RFC does not depend on that fix.

Entries that used a *_minmax handler with neither extra pointer relied on
NULL meaning "no bound". They now state their type limits explicitly to
preserve that behaviour when the pointer path disappears. These include
aio-max-nr, acpi_video_flags and nmi_wd_lpm_factor.

Owners that can still be recovered from data are deliberately left alone:
ipvs, neighbour, the fragment reassembly handlers, vsock, mptcp, rds and
smc keep using container_of() on their backing storage. priv is only for
entries where no such derivation exists. Mixed placement in data and priv
was considered and rejected because it recreates the guessing problem this
series removes.

Relative to the base tree, the final state removes all 841 extra1/extra2
assignments across 123 files, removes the shared sysctl_vals and
sysctl_long_vals arrays (21 references), and states bounds at each entry
(~574 new min/max initializers) instead of pointing at constants. The
sysctl ABI is unchanged; the parport spintime minimum is corrected as
described above.

The cost is 8 bytes per ctl_table entry on 64-bit (56 -> 64) and 4 bytes on
32-bit, because priv is appended; the range read path gains no indirection
and the migrated entries no longer dereference a bound pointer.

Wen Yang (39):
  sysctl: replace overloaded extra pointers with typed bounds and
    context
  test_sysctl: convert bounds to numeric values
  sysctl: add KUnit coverage for numeric bounds and handlers
  sysctl: cover runtime ranges and context lifecycles in KUnit
  sysctl: cover priv in KUnit and the debug test module
  sysctl: convert core table bounds to numeric values
  sysctl: store architecture sysctl bounds by value
  sysctl: store crypto sysctl bounds by value
  sysctl: store device sysctl bounds by value
  sysctl: store filesystem sysctl bounds by value
  sysctl: store message-queue sysctl bounds by value
  sysctl: store kernel sysctl bounds by value
  sysctl: store memory-management sysctl bounds by value
  sysctl: store IPv6 scalar sysctl bounds by value
  sysctl: store netfilter scalar sysctl bounds by value
  sysctl: store RPC sysctl bounds by value
  sysctl: store remaining network sysctl bounds by value
  sysctl: store security sysctl bounds by value
  io_uring: store its sysctl bounds by value
  sysctl: refresh dynamic scheduler and PID bounds per call
  sysctl: refresh device-dependent bounds per call
  sysctl: refresh namespace IPC bounds per call
  yama: preserve scope locking with numeric sysctl bounds
  net: refresh core sysctl limits without bound pointers
  sctp: resolve numeric ranges from the current namespace
  rds: preserve live reconnect limits with numeric bounds
  ipv4: preserve live and vector bounds with numeric sysctls
  net: refresh fragment and remaining dynamic sysctl bounds
  neighbour: derive sysctl ownership independently of bounds
  net: resolve sysctl owners from their backing data
  ipvs: derive sysctl ownership from numeric storage
  sysctl: remove obsolete nr_open bound variables
  ipv6: move address sysctl context to priv
  ipv4: move device sysctl context to priv
  netfilter: move logger sysctl context to priv
  parport: move device sysctl context to priv
  net: move namespace sysctl context to priv
  arm64: move vector-length sysctl context to priv
  sysctl: remove the extra pointers and the compatibility bounds read

 arch/arm64/kernel/armv8_deprecated.c          |    4 +-
 arch/arm64/kernel/fpsimd.c                    |   13 +-
 arch/arm64/kernel/process.c                   |    4 +-
 arch/powerpc/platforms/pseries/mobility.c     |    3 +
 arch/riscv/kernel/process.c                   |    4 +-
 arch/s390/appldata/appldata_base.c            |   12 +-
 arch/s390/kernel/hiperdispatch.c              |    4 +-
 arch/s390/kernel/topology.c                   |    8 +-
 arch/sh/kernel/vsyscall/vsyscall.c            |    4 +-
 arch/x86/entry/vdso/vdso32-setup.c            |    4 +-
 arch/x86/kernel/cpu/bus_lock.c                |    4 +-
 arch/x86/kernel/setup.c                       |    3 +
 crypto/af_alg.c                               |    4 +-
 drivers/base/firmware_loader/fallback_table.c |    8 +-
 drivers/base/power/main.c                     |   21 +-
 drivers/gpu/drm/i915/i915_perf.c              |   19 +-
 drivers/gpu/drm/xe/xe_observation.c           |    4 +-
 drivers/hv/hv_common.c                        |    4 +-
 drivers/infiniband/core/iwcm.c                |    5 +-
 drivers/infiniband/core/ucma.c                |    5 +-
 drivers/net/vrf.c                             |   20 +-
 drivers/parport/procfs.c                      |   51 +-
 drivers/perf/arm_pmuv3.c                      |    4 +-
 drivers/perf/riscv_pmu_sbi.c                  |    4 +-
 drivers/scsi/scsi_sysctl.c                    |    5 +-
 drivers/tty/tty_io.c                          |    4 +-
 drivers/xen/balloon.c                         |    4 +-
 fs/aio.c                                      |    5 +
 fs/coredump.c                                 |   15 +-
 fs/dcache.c                                   |   13 +-
 fs/devpts/inode.c                             |   10 +-
 fs/drop_caches.c                              |    4 +-
 fs/eventpoll.c                                |    7 +-
 fs/exec.c                                     |   10 +-
 fs/file.c                                     |    5 -
 fs/file_table.c                               |   12 +-
 fs/fs-writeback.c                             |    4 +-
 fs/fuse/sysctl.c                              |   24 +-
 fs/lockd/svc.c                                |   18 +-
 fs/namei.c                                    |   16 +-
 fs/namespace.c                                |    3 +-
 fs/nfs/nfs4sysctl.c                           |    6 +-
 fs/notify/fanotify/fanotify_user.c            |   17 +-
 fs/notify/inotify/inotify_user.c              |   14 +-
 fs/pipe.c                                     |   11 +-
 fs/proc/proc_sysctl.c                         |   62 +-
 fs/sysctls.c                                  |    8 +-
 fs/verity/init.c                              |    4 +-
 fs/xfs/xfs_sysctl.c                           |   59 +-
 include/linux/file.h                          |    2 -
 include/linux/sysctl.h                        |   72 +-
 include/net/neighbour.h                       |    3 +
 io_uring/io_uring.c                           |    4 +-
 ipc/ipc_sysctl.c                              |   53 +-
 ipc/mq_sysctl.c                               |   22 +-
 kernel/bpf/syscall.c                          |    8 +-
 kernel/delayacct.c                            |    4 +-
 kernel/events/callchain.c                     |    9 +-
 kernel/events/core.c                          |    7 +-
 kernel/fork.c                                 |    8 +-
 kernel/hung_task.c                            |   30 +-
 kernel/kexec_core.c                           |    4 +-
 kernel/kprobes.c                              |    4 +-
 kernel/kstack_erase.c                         |    4 +-
 kernel/module/main.c                          |    4 +-
 kernel/panic.c                                |   13 +-
 kernel/pid.c                                  |   16 +-
 kernel/pid_namespace.c                        |    6 +-
 kernel/pid_sysctl.h                           |    6 +-
 kernel/printk/sysctl.c                        |   22 +-
 kernel/rcu/tree_stall.h                       |    9 +-
 kernel/sched/autogroup.c                      |    4 +-
 kernel/sched/core.c                           |    8 +-
 kernel/sched/deadline.c                       |   29 +-
 kernel/sched/fair.c                           |    6 +-
 kernel/sched/rt.c                             |   13 +-
 kernel/sched/topology.c                       |    4 +-
 kernel/sys.c                                  |    8 +-
 kernel/sysctl-test.c                          | 1357 +++++++++++++++--
 kernel/sysctl.c                               |   43 +-
 kernel/time/jiffies.c                         |    3 +-
 kernel/time/timer.c                           |    4 +-
 kernel/ucount.c                               |    7 +-
 kernel/watchdog.c                             |   35 +-
 lib/test_sysctl.c                             |  135 +-
 mm/compaction.c                               |   19 +-
 mm/filemap.c                                  |    4 +-
 mm/memory-failure.c                           |   16 +-
 mm/mmap.c                                     |   28 +-
 mm/nommu.c                                    |    4 +-
 mm/oom_kill.c                                 |    4 +-
 mm/page-writeback.c                           |   25 +-
 mm/page_alloc.c                               |   28 +-
 mm/swap_state.c                               |    5 +-
 mm/userfaultfd.c                              |    4 +-
 mm/util.c                                     |   10 +-
 mm/vmscan.c                                   |    8 +-
 mm/vmstat.c                                   |    4 +-
 net/core/neighbour.c                          |  101 +-
 net/core/sysctl_net_core.c                    |  129 +-
 net/ieee802154/6lowpan/reassembly.c           |   30 +-
 net/ipv4/devinet.c                            |   53 +-
 net/ipv4/ip_fragment.c                        |   34 +-
 net/ipv4/route.c                              |    8 +-
 net/ipv4/sysctl_net_ipv4.c                    |  408 +++--
 net/ipv6/addrconf.c                           |  163 +-
 net/ipv6/icmp.c                               |   17 +-
 net/ipv6/ndisc.c                              |    3 +-
 net/ipv6/netfilter/nf_conntrack_reasm.c       |   30 +-
 net/ipv6/reassembly.c                         |   30 +-
 net/ipv6/route.c                              |   11 +-
 net/ipv6/sysctl_net_ipv6.c                    |   53 +-
 net/mpls/af_mpls.c                            |   58 +-
 net/mptcp/ctrl.c                              |   29 +-
 net/netfilter/ipvs/ip_vs_ctl.c                |  157 +-
 net/netfilter/nf_conntrack_standalone.c       |   45 +-
 net/netfilter/nf_hooks_lwtunnel.c             |    4 +-
 net/netfilter/nf_log.c                        |   67 +-
 net/phonet/sysctl.c                           |    6 +-
 net/rds/ib_sysctl.c                           |   23 +-
 net/rds/sysctl.c                              |   32 +-
 net/rds/tcp.c                                 |   11 +-
 net/rxrpc/sysctl.c                            |   51 +-
 net/sctp/sysctl.c                             |  148 +-
 net/smc/smc_sysctl.c                          |   42 +-
 net/sunrpc/xprtrdma/svc_rdma.c                |   39 +-
 net/sunrpc/xprtrdma/transport.c               |   27 +-
 net/sunrpc/xprtsock.c                         |   26 +-
 net/tipc/sysctl.c                             |   18 +-
 net/vmw_vsock/af_vsock.c                      |    4 +-
 net/x25/sysctl_net_x25.c                      |   23 +-
 security/keys/sysctl.c                        |   25 +-
 security/loadpin/loadpin.c                    |    4 +-
 security/yama/yama_lsm.c                      |   10 +-
 134 files changed, 3075 insertions(+), 1525 deletions(-)

-- 
2.25.1


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

* [RFC PATCH 01/39] sysctl: replace overloaded extra pointers with typed bounds and context
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 21:09   ` David Laight
  2026-10-28 16:34 ` [RFC PATCH 02/39] test_sysctl: convert bounds to numeric values wen.yang
                   ` (37 subsequent siblings)
  38 siblings, 1 reply; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

struct ctl_table has two untyped misc pointers, extra1 and extra2, whose
meaning depends on the handler that reads them. In-tree use has grown to
cover at least seven unrelated jobs: a fixed bound, a bound read through a
pointer on every call, an object or namespace pointer, a small integer
cookie encoded into the pointer, a lock signal, NULL standing for "no
bound", and a value the core itself casts to unsigned int while validating
a u8 range. Nothing in the entry says which job applies, so readers and
writers have to infer it from the handler, and the core itself gets it
wrong where it treats extra1 as an unsigned int.

The overloading is also a memory-safety hazard. An entry that stores
handler context in data cannot be handed to a standard handler without
first restoring the real storage, because proc_dointvec() writes through
data. Every call site has to remember the restore, and one that forgets
turns a user-supplied integer into a write over a kernel pointer. nf_log
reaches the same problem from the other side: it replaces data with a
local buffer inside a single handler, so data means two things within one
function.

Replace both jobs with two fields that cannot be confused:

  - min/max hold numeric bounds by value. Signed handlers read them as
    signed longs; an absent endpoint is the type limit, not NULL, so zero
    stays a real bound. A range that changes at run time is refreshed
    before every call instead of being snapshotted at registration.

  - priv holds handler context. The core never reads it, standard handlers
    never touch it, and registration rejects it on a standard handler so
    context cannot be silently ignored. data and maxlen keep the meaning
    their handler gives them, including proc_dostring() name lengths and
    proc_do_large_bitmap() bit counts.

The range converters read min/max whenever extra1 and extra2 are both
clear, and keep reading the pointers while they are set. That lets callers
migrate one subsystem at a time without renaming handlers or adding a
parallel API: the existing proc_*_minmax entry points keep their names and
gain numeric semantics as entries drop their extras. Entries that already
relied on NULL meaning "no bound" now state their type limits explicitly,
so their behaviour does not change and the meaning of zero stays
unambiguous.

This patch is the refactor itself. The next patch converts the debug test
module to numeric bounds as the first user, and the KUnit suite after it
makes the contract executable; everything after those is migration of
existing users and adds no new semantics: fixed bounds become numeric
values, run-time bounds are refreshed per call, context moves off data, and
the extra pointers and the compatibility read are removed once the last
user is gone. Owners that can still be recovered from data keep using
container_of(); priv is only for entries where no derivation exists.

Replacing the two pointers with unsigned long min/max follows Eric
Biederman's suggestion; priv is added here because the same pair of fields
also carries handler context, which a numeric bound cannot express.

Suggested-by: Eric W. Biederman <ebiederm@xmission.com>
Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 arch/powerpc/platforms/pseries/mobility.c |  3 +
 arch/x86/kernel/setup.c                   |  3 +
 fs/aio.c                                  |  5 ++
 fs/pipe.c                                 |  5 ++
 fs/proc/proc_sysctl.c                     | 44 +++++++++----
 include/linux/sysctl.h                    | 11 ++--
 ipc/ipc_sysctl.c                          |  5 ++
 kernel/sysctl.c                           | 36 ++++++-----
 lib/test_sysctl.c                         |  3 +
 mm/util.c                                 |  5 ++
 net/ieee802154/6lowpan/reassembly.c       |  5 ++
 net/ipv4/ip_fragment.c                    |  5 ++
 net/ipv4/sysctl_net_ipv4.c                | 79 +++++++++++++++++++++++
 net/ipv6/icmp.c                           |  7 ++
 net/ipv6/netfilter/nf_conntrack_reasm.c   |  5 ++
 net/ipv6/reassembly.c                     |  5 ++
 net/ipv6/sysctl_net_ipv6.c                | 13 ++++
 net/mptcp/ctrl.c                          |  5 ++
 net/netfilter/ipvs/ip_vs_ctl.c            |  3 +
 net/netfilter/nf_conntrack_standalone.c   |  3 +
 net/rds/ib_sysctl.c                       |  3 +
 net/sctp/sysctl.c                         |  3 +
 net/tipc/sysctl.c                         |  5 ++
 23 files changed, 230 insertions(+), 31 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c
index b5c2abd12432..294897f3fbb5 100644
--- a/arch/powerpc/platforms/pseries/mobility.c
+++ b/arch/powerpc/platforms/pseries/mobility.c
@@ -10,6 +10,7 @@
 #define pr_fmt(fmt) "mobility: " fmt
 
 #include <linux/cpu.h>
+#include <linux/limits.h>
 #include <linux/kernel.h>
 #include <linux/kobject.h>
 #include <linux/nmi.h>
@@ -61,6 +62,8 @@ static const struct ctl_table nmi_wd_lpm_factor_ctl_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
+		.min		= 0,
+		.max		= UINT_MAX,
 	},
 };
 
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index e352e46ef2e5..91c49c9eeaed 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -6,6 +6,7 @@
  * parts of early kernel initialization.
  */
 #include <linux/acpi.h>
+#include <linux/limits.h>
 #include <linux/bootconfig.h>
 #include <linux/console.h>
 #include <linux/cpu.h>
@@ -199,6 +200,8 @@ static const struct ctl_table x86_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 #endif
 };
diff --git a/fs/aio.c b/fs/aio.c
index 8a62b770a70e..2ae0fcc00f78 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -12,6 +12,7 @@
 #define pr_fmt(fmt) "%s: " fmt, __func__
 
 #include <linux/kernel.h>
+#include <linux/limits.h>
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/time.h>
@@ -243,6 +244,8 @@ static const struct ctl_table aio_sysctls[] = {
 		.maxlen		= sizeof(aio_nr),
 		.mode		= 0444,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "aio-max-nr",
@@ -250,6 +253,8 @@ static const struct ctl_table aio_sysctls[] = {
 		.maxlen		= sizeof(aio_max_nr),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 };
 
diff --git a/fs/pipe.c b/fs/pipe.c
index 4fa69192e6fb..c41ab753c893 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -6,6 +6,7 @@
  */
 
 #include <linux/mm.h>
+#include <linux/limits.h>
 #include <linux/file.h>
 #include <linux/poll.h>
 #include <linux/slab.h>
@@ -1604,6 +1605,8 @@ static const struct ctl_table fs_pipe_sysctls[] = {
 		.maxlen		= sizeof(pipe_user_pages_hard),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "pipe-user-pages-soft",
@@ -1611,6 +1614,8 @@ static const struct ctl_table fs_pipe_sysctls[] = {
 		.maxlen		= sizeof(pipe_user_pages_soft),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 };
 #endif
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 6c0859c66563..3e65d75710df 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -1132,6 +1132,13 @@ static int sysctl_check_table_array(const char *path, const struct ctl_table *ta
 		}
 	}
 
+	/* Numeric bounds are validated alongside the legacy pointer form. */
+	if (table->proc_handler == proc_dou8vec_minmax) {
+		if (table->min > 255U || table->max > 255U)
+			err |= sysctl_err(path, table,
+					 "range value too large for proc_dou8vec_minmax");
+	}
+
 	if (table->proc_handler == proc_dobool) {
 		if (table->maxlen != sizeof(bool))
 			err |= sysctl_err(path, table, "array not allowed");
@@ -1140,31 +1147,44 @@ static int sysctl_check_table_array(const char *path, const struct ctl_table *ta
 	return err;
 }
 
+/* Standard handlers only interpret data, maxlen and min/max. */
+static bool sysctl_is_standard_handler(proc_handler *handler)
+{
+	return handler == proc_dostring ||
+	       handler == proc_dobool ||
+	       handler == proc_dointvec ||
+	       handler == proc_douintvec ||
+	       handler == proc_dointvec_jiffies ||
+	       handler == proc_dointvec_userhz_jiffies ||
+	       handler == proc_dointvec_ms_jiffies ||
+	       handler == proc_dointvec_minmax ||
+	       handler == proc_douintvec_minmax ||
+	       handler == proc_doulongvec_minmax ||
+	       handler == proc_dou8vec_minmax ||
+	       handler == proc_doulongvec_ms_jiffies_minmax;
+}
+
 static int sysctl_check_table(const char *path, struct ctl_table_header *header)
 {
 	const struct ctl_table *entry;
+	bool standard;
 	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) ||
-		    (entry->proc_handler == proc_douintvec) ||
-		    (entry->proc_handler == proc_douintvec_minmax) ||
-		    (entry->proc_handler == proc_dointvec_minmax) ||
-		    (entry->proc_handler == proc_dou8vec_minmax) ||
-		    (entry->proc_handler == proc_dointvec_jiffies) ||
-		    (entry->proc_handler == proc_dointvec_userhz_jiffies) ||
-		    (entry->proc_handler == proc_dointvec_ms_jiffies) ||
-		    (entry->proc_handler == proc_doulongvec_minmax) ||
-		    (entry->proc_handler == proc_doulongvec_ms_jiffies_minmax)) {
+		standard = sysctl_is_standard_handler(entry->proc_handler);
+		if (standard) {
 			if (!entry->data)
 				err |= sysctl_err(path, entry, "No data");
 			if (!entry->maxlen)
 				err |= sysctl_err(path, entry, "No maxlen");
 			else
 				err |= sysctl_check_table_array(path, entry);
+			/* priv belongs to custom handlers; catching this
+			 * early keeps context from being silently ignored.
+			 */
+			if (entry->priv)
+				err |= sysctl_err(path, entry, "priv requires a custom handler");
 		}
 		if (!entry->proc_handler)
 			err |= sysctl_err(path, entry, "No proc_handler");
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index e5d7226ab6f5..225c07faf2c9 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -221,13 +221,16 @@ static inline void *proc_sys_poll_event(struct ctl_table_poll *poll)
 /* A sysctl table is an array of struct ctl_table: */
 struct ctl_table {
 	const char *procname;		/* Text ID for /proc/sys */
-	void *data;
-	int maxlen;
+	void *data;			/* Value or buffer; handler-defined */
+	int maxlen;			/* Length; handler-defined */
 	umode_t mode;
 	proc_handler *proc_handler;	/* Callback for text formatting */
 	struct ctl_table_poll *poll;
-	void *extra1;
-	void *extra2;
+	void *extra1;			/* Deprecated; being removed */
+	void *extra2;			/* Deprecated; being removed */
+	unsigned long min;
+	unsigned long max;
+	void *priv;			/* Handler context, never used by the core */
 } __randomize_layout;
 
 struct ctl_node {
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c
index d038d944257f..e02e2ea70907 100644
--- a/ipc/ipc_sysctl.c
+++ b/ipc/ipc_sysctl.c
@@ -6,6 +6,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/limits.h>
 #include <linux/ipc.h>
 #include <linux/nsproxy.h>
 #include <linux/sysctl.h>
@@ -80,6 +81,8 @@ static const struct ctl_table ipc_sysctls[] = {
 		.maxlen		= sizeof(init_ipc_ns.shm_ctlmax),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "shmall",
@@ -87,6 +90,8 @@ static const struct ctl_table ipc_sysctls[] = {
 		.maxlen		= sizeof(init_ipc_ns.shm_ctlall),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "shmmni",
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f5145b32f6be..7d0bcd2b24ef 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -423,11 +423,15 @@ int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
 		ret = user_to_kern(u_ptr, &tmp_k);
 		if (ret)
 			return ret;
-		if ((tbl->extra1 &&
-		     *(uint *)tbl->extra1 > tmp_k) ||
-		    (tbl->extra2 &&
-		     *(uint *)tbl->extra2 < tmp_k))
+		if (tbl->extra1 || tbl->extra2) {
+			if ((tbl->extra1 &&
+			     *(uint *)tbl->extra1 > tmp_k) ||
+			    (tbl->extra2 &&
+			     *(uint *)tbl->extra2 < tmp_k))
+				return -ERANGE;
+		} else if (tbl->min > tmp_k || tbl->max < tmp_k) {
 			return -ERANGE;
+		}
 		WRITE_ONCE(*k_ptr, tmp_k);
 	} else
 		return user_to_kern(u_ptr, k_ptr);
@@ -543,17 +547,19 @@ int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir,
 		ret = user_to_kern(negp, u_ptr, &tmp_k);
 		if (ret)
 			return ret;
-		if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) ||
-		    (tbl->extra2 && *(int *)tbl->extra2 < tmp_k))
+		if (tbl->extra1 || tbl->extra2) {
+			if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) ||
+			    (tbl->extra2 && *(int *)tbl->extra2 < tmp_k))
+				return -EINVAL;
+		} else if ((long)tbl->min > tmp_k || (long)tbl->max < tmp_k) {
 			return -EINVAL;
+		}
 		WRITE_ONCE(*k_ptr, tmp_k);
 	} else
 		return user_to_kern(negp, u_ptr, k_ptr);
 	return 0;
 }
 
-
-
 static int sysctl_user_to_kern_int_conv(const bool *negp, const ulong *u_ptr,
 					int *k_ptr)
 {
@@ -985,7 +991,7 @@ int proc_dou8vec_minmax(const struct ctl_table *table, int dir,
 			void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table tmp;
-	unsigned int min = 0, max = 255U, val;
+	unsigned int val;
 	u8 *data = table->data;
 	int res;
 
@@ -997,10 +1003,6 @@ int proc_dou8vec_minmax(const struct ctl_table *table, int dir,
 
 	tmp.maxlen = sizeof(val);
 	tmp.data = &val;
-	if (!tmp.extra1)
-		tmp.extra1 = (unsigned int *) &min;
-	if (!tmp.extra2)
-		tmp.extra2 = (unsigned int *) &max;
 
 	val = READ_ONCE(*data);
 	res = proc_douintvec_minmax(&tmp, dir, buffer, lenp, ppos);
@@ -1045,9 +1047,13 @@ int proc_ulong_conv(ulong *u_ptr, ulong *k_ptr, int dir,
 		ret = user_to_kern(u_ptr, &tmp_k);
 		if (ret)
 			return ret;
-		if ((tbl->extra1 && *(ulong *)tbl->extra1 > tmp_k) ||
-		    (tbl->extra2 && *(ulong *)tbl->extra2 < tmp_k))
+		if (tbl->extra1 || tbl->extra2) {
+			if ((tbl->extra1 && *(ulong *)tbl->extra1 > tmp_k) ||
+			    (tbl->extra2 && *(ulong *)tbl->extra2 < tmp_k))
+				return -ERANGE;
+		} else if (tbl->min > tmp_k || tbl->max < tmp_k) {
 			return -ERANGE;
+		}
 		WRITE_ONCE(*k_ptr, tmp_k);
 	} else
 		return user_to_kern(u_ptr, k_ptr);
diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c
index 65ce42346170..eed2cc1bea82 100644
--- a/lib/test_sysctl.c
+++ b/lib/test_sysctl.c
@@ -15,6 +15,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/init.h>
+#include <linux/limits.h>
 #include <linux/jiffies.h>
 #include <linux/list.h>
 #include <linux/module.h>
@@ -217,6 +218,8 @@ static const struct ctl_table test_table_unregister[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
+		.min		= INT_MIN,
+		.max		= INT_MAX,
 	},
 };
 
diff --git a/mm/util.c b/mm/util.c
index 8acfe2148ba5..2f2bc521868a 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 #include <linux/mm.h>
+#include <linux/limits.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/compiler.h>
@@ -880,6 +881,8 @@ static const struct ctl_table util_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_user_reserve_kbytes),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "admin_reserve_kbytes",
@@ -887,6 +890,8 @@ static const struct ctl_table util_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_admin_reserve_kbytes),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 };
 
diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
index ddb6a5817d09..a2167f76ecc3 100644
--- a/net/ieee802154/6lowpan/reassembly.c
+++ b/net/ieee802154/6lowpan/reassembly.c
@@ -10,6 +10,7 @@
 #define pr_fmt(fmt) "6LoWPAN: " fmt
 
 #include <linux/net.h>
+#include <linux/limits.h>
 #include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/random.h>
@@ -332,12 +333,16 @@ static struct ctl_table lowpan_frags_ns_ctl_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "6lowpanfrag_low_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "6lowpanfrag_time",
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index c790d2f49487..75bc675e5579 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -24,6 +24,7 @@
 #define pr_fmt(fmt) "IPv4: " fmt
 
 #include <linux/compiler.h>
+#include <linux/limits.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/mm.h>
@@ -551,12 +552,16 @@ static struct ctl_table ip4_frags_ns_ctl_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "ipfrag_low_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "ipfrag_time",
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index e3760daa3470..2ed9f59ea747 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/sysctl.h>
+#include <linux/limits.h>
 #include <linux/seqlock.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -564,6 +565,8 @@ static struct ctl_table ipv4_table[] = {
 		.data		= &sysctl_tcp_mem,
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "tcp_low_latency",
@@ -614,6 +617,8 @@ static struct ctl_table ipv4_table[] = {
 		.maxlen		= sizeof(sysctl_udp_mem),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "fib_sync_mem",
@@ -778,6 +783,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "ip_early_demux",
@@ -785,6 +792,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname       = "udp_early_demux",
@@ -792,6 +801,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
+		.min           = 0,
+		.max           = U8_MAX,
 	},
 	{
 		.procname       = "tcp_early_demux",
@@ -799,6 +810,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
+		.min           = 0,
+		.max           = U8_MAX,
 	},
 	{
 		.procname       = "nexthop_compat_mode",
@@ -845,6 +858,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "ip_forward_use_pmtu",
@@ -852,6 +867,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "ip_forward_update_priority",
@@ -868,6 +885,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "ip_autobind_reuse",
@@ -884,6 +903,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_fwmark_accept",
@@ -891,6 +912,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 #ifdef CONFIG_NET_L3_MASTER_DEV
 	{
@@ -909,6 +932,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_base_mss",
@@ -956,6 +981,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "igmp_max_memberships",
@@ -1013,6 +1040,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_keepalive_intvl",
@@ -1036,6 +1065,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 #ifdef CONFIG_SYN_COOKIES
 	{
@@ -1044,6 +1075,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 #endif
 	{
@@ -1078,6 +1111,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_orphan_retries",
@@ -1085,6 +1120,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_fin_timeout",
@@ -1212,6 +1249,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_window_scaling",
@@ -1219,6 +1258,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_timestamps",
@@ -1226,6 +1267,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_early_retrans",
@@ -1242,6 +1285,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname       = "tcp_thin_linear_timeouts",
@@ -1249,6 +1294,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
+		.min           = 0,
+		.max           = U8_MAX,
 	},
 	{
 		.procname	= "tcp_slow_start_after_idle",
@@ -1256,6 +1303,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_retrans_collapse",
@@ -1263,6 +1312,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_stdurg",
@@ -1270,6 +1321,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_rfc1337",
@@ -1277,6 +1330,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_abort_on_overflow",
@@ -1284,6 +1339,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_fack",
@@ -1291,6 +1348,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_max_reordering",
@@ -1306,6 +1365,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_app_win",
@@ -1331,6 +1392,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_no_metrics_save",
@@ -1338,6 +1401,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_no_ssthresh_metrics_save",
@@ -1354,6 +1419,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_rcvbuf_low_rtt",
@@ -1370,6 +1437,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_workaround_signed_windows",
@@ -1377,6 +1446,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_limit_output_bytes",
@@ -1406,6 +1477,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_min_rtt_wlen",
@@ -1472,6 +1545,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "tcp_comp_sack_rtt_percent",
@@ -1488,6 +1563,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "tcp_comp_sack_nr",
@@ -1601,6 +1678,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
+		.min           = 0,
+		.max           = U8_MAX,
 	},
 	{
 		.procname       = "tcp_plb_cong_thresh",
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index a95b0351824f..1ba2ff759075 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -28,6 +28,7 @@
 #define pr_fmt(fmt) "IPv6: " fmt
 
 #include <linux/module.h>
+#include <linux/limits.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
@@ -1388,6 +1389,8 @@ static const struct ctl_table ipv6_icmp_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler = proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "echo_ignore_multicast",
@@ -1395,6 +1398,8 @@ static const struct ctl_table ipv6_icmp_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler = proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "echo_ignore_anycast",
@@ -1402,6 +1407,8 @@ static const struct ctl_table ipv6_icmp_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler = proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "ratemask",
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index be72c4346f8b..1f3ba85f6dca 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -13,6 +13,7 @@
 #define pr_fmt(fmt) "IPv6-nf: " fmt
 
 #include <linux/errno.h>
+#include <linux/limits.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/net.h>
@@ -55,12 +56,16 @@ static struct ctl_table nf_ct_frag6_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "nf_conntrack_frag6_high_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 };
 
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index a7964125c764..70c845385744 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -26,6 +26,7 @@
 #define pr_fmt(fmt) "IPv6: " fmt
 
 #include <linux/errno.h>
+#include <linux/limits.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/socket.h>
@@ -427,12 +428,16 @@ static struct ctl_table ip6_frags_ns_ctl_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "ip6frag_low_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "ip6frag_time",
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index 1a0a36dcdabc..c870a364de60 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/mm.h>
+#include <linux/limits.h>
 #include <linux/sysctl.h>
 #include <linux/in6.h>
 #include <linux/ipv6.h>
@@ -68,6 +69,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "anycast_src_echo_reply",
@@ -75,6 +78,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "flowlabel_consistency",
@@ -82,6 +87,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "auto_flowlabels",
@@ -97,6 +104,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "idgen_retries",
@@ -118,6 +127,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "ip_nonlocal_bind",
@@ -125,6 +136,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "flowlabel_reflect",
diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c
index 63c5747f0f63..e5a5995c5753 100644
--- a/net/mptcp/ctrl.c
+++ b/net/mptcp/ctrl.c
@@ -6,6 +6,7 @@
 
 #ifdef CONFIG_SYSCTL
 #include <linux/sysctl.h>
+#include <linux/limits.h>
 #endif
 
 #include <net/net_namespace.h>
@@ -317,6 +318,8 @@ static struct ctl_table mptcp_sysctl_table[] = {
 		.maxlen = sizeof(unsigned int),
 		.mode = 0644,
 		.proc_handler = proc_douintvec_minmax,
+		.min = 0,
+		.max = UINT_MAX,
 	},
 	{
 		.procname = "pm_type",
@@ -356,6 +359,8 @@ static struct ctl_table mptcp_sysctl_table[] = {
 		.maxlen = sizeof(u8),
 		.mode = 0644,
 		.proc_handler = proc_dou8vec_minmax,
+		.min = 0,
+		.max = U8_MAX,
 	},
 	{
 		.procname = "path_manager",
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index ce9fe20d6106..6c29a901d139 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -16,6 +16,7 @@
 #define pr_fmt(fmt) "IPVS: " fmt
 
 #include <linux/module.h>
+#include <linux/limits.h>
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/capability.h>
@@ -2778,6 +2779,8 @@ static struct ctl_table vs_vars[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "sync_sock_size",
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index d88a10230862..561db3aecada 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/types.h>
+#include <linux/limits.h>
 #include <linux/netfilter.h>
 #include <linux/slab.h>
 #include <linux/module.h>
@@ -821,6 +822,8 @@ static const struct ctl_table nf_ct_sysctl_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP] = {
 		.procname	= "nf_conntrack_udp_timeout",
diff --git a/net/rds/ib_sysctl.c b/net/rds/ib_sysctl.c
index 2af678e71e3c..b7757f31001c 100644
--- a/net/rds/ib_sysctl.c
+++ b/net/rds/ib_sysctl.c
@@ -31,6 +31,7 @@
  *
  */
 #include <linux/kernel.h>
+#include <linux/limits.h>
 #include <linux/sysctl.h>
 #include <linux/proc_fs.h>
 
@@ -95,6 +96,8 @@ static struct ctl_table rds_ib_sysctl_table[] = {
 		.maxlen         = sizeof(unsigned long),
 		.mode           = 0644,
 		.proc_handler   = proc_doulongvec_minmax,
+		.min           = 0,
+		.max           = ULONG_MAX,
 	},
 	{
 		.procname	= "flow_control",
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 2b94c211427d..59c6f22337ea 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -24,6 +24,7 @@
 #include <net/sctp/structs.h>
 #include <net/sctp/sctp.h>
 #include <linux/sysctl.h>
+#include <linux/limits.h>
 
 static int timer_max = 86400000; /* ms in one day */
 static int sack_timer_min = 1;
@@ -65,6 +66,8 @@ static struct ctl_table sctp_table[] = {
 		.maxlen		= sizeof(sysctl_sctp_mem),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "sctp_rmem",
diff --git a/net/tipc/sysctl.c b/net/tipc/sysctl.c
index 30d2e06e3d8c..8352c790c8fe 100644
--- a/net/tipc/sysctl.c
+++ b/net/tipc/sysctl.c
@@ -38,6 +38,7 @@
 #include "crypto.h"
 #include "bcast.h"
 #include <linux/sysctl.h>
+#include <linux/limits.h>
 
 static struct ctl_table_header *tipc_ctl_hdr;
 
@@ -64,6 +65,8 @@ static struct ctl_table tipc_table[] = {
 		.maxlen         = sizeof(sysctl_tipc_sk_filter),
 		.mode           = 0644,
 		.proc_handler   = proc_doulongvec_minmax,
+		.min           = 0,
+		.max           = ULONG_MAX,
 	},
 #ifdef CONFIG_TIPC_CRYPTO
 	{
@@ -90,6 +93,8 @@ static struct ctl_table tipc_table[] = {
 		.maxlen		= sizeof(sysctl_tipc_bc_retruni),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 };
 
-- 
2.25.1


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

* [RFC PATCH 02/39] test_sysctl: convert bounds to numeric values
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
  2026-10-28 16:34 ` [RFC PATCH 01/39] " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 03/39] sysctl: add KUnit coverage for numeric bounds and handlers wen.yang
                   ` (36 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

The debug module still passes extra pointers for its numeric ranges.
Convert them to numeric min/max so it exercises the same shape as the
rest of the tree, and keep the dynamic-value entry's context in data for
now; the priv conversion comes later.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 lib/test_sysctl.c | 135 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 109 insertions(+), 26 deletions(-)

diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c
index eed2cc1bea82..1411896fdbd0 100644
--- a/lib/test_sysctl.c
+++ b/lib/test_sysctl.c
@@ -15,7 +15,6 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/init.h>
-#include <linux/limits.h>
 #include <linux/jiffies.h>
 #include <linux/list.h>
 #include <linux/module.h>
@@ -28,8 +27,6 @@
 #include <linux/delay.h>
 #include <linux/vmalloc.h>
 
-static int i_zero;
-static int i_one_hundred = 100;
 static int match_int_ok = 1;
 
 enum {
@@ -43,6 +40,12 @@ enum {
 };
 
 static struct ctl_table_header *ctl_headers[TEST_H_SIZE] = {};
+
+struct test_sysctl_range_context {
+	int value;
+	int limit;
+};
+
 struct test_sysctl_data {
 	int int_0001;
 	int int_0002;
@@ -56,6 +59,11 @@ struct test_sysctl_data {
 
 	unsigned int uint_0001;
 
+	struct test_sysctl_range_context dynamic;
+	int int_zero;
+	unsigned long ulong_zero;
+	u8 u8_zero;
+
 	char string_0001[65];
 
 #define SYSCTL_TEST_BITMAP_SIZE	65536
@@ -80,9 +88,25 @@ static struct test_sysctl_data test_data = {
 
 	.uint_0001 = 314,
 
+	.dynamic = { .value = 0, .limit = 100 },
+
 	.string_0001 = "(none)",
 };
 
+/* The registered data is typed context, not the backing integer. */
+static int test_sysctl_dynamic_value(const struct ctl_table *table, int dir,
+				     void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct test_sysctl_range_context *context = table->data;
+	struct ctl_table resolved = *table;
+
+	resolved.data = &context->value;
+	resolved.maxlen = sizeof(context->value);
+	resolved.min = INT_MIN;
+	resolved.max = READ_ONCE(context->limit);
+	return proc_dointvec_minmax(&resolved, dir, buffer, lenp, ppos);
+}
+
 /* These are all under /proc/sys/debug/test_sysctl/ */
 static const struct ctl_table test_table[] = {
 	{
@@ -91,8 +115,8 @@ static const struct ctl_table test_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &i_zero,
-		.extra2         = &i_one_hundred,
+		.min		= 0,
+		.max		= 100,
 	},
 	{
 		.procname	= "int_0002",
@@ -100,6 +124,8 @@ static const struct ctl_table test_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
+		.min		= INT_MIN,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "int_0003",
@@ -107,6 +133,8 @@ static const struct ctl_table test_table[] = {
 		.maxlen		= sizeof(test_data.int_0003),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
+		.min		= INT_MIN,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "match_int",
@@ -114,6 +142,8 @@ static const struct ctl_table test_table[] = {
 		.maxlen		= sizeof(match_int_ok),
 		.mode		= 0444,
 		.proc_handler	= proc_dointvec,
+		.min		= INT_MIN,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "boot_int",
@@ -121,8 +151,8 @@ static const struct ctl_table test_table[] = {
 		.maxlen		= sizeof(test_data.boot_int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "int_jiffies",
@@ -130,6 +160,8 @@ static const struct ctl_table test_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_jiffies,
+		.min		= INT_MIN,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "int_ms_jiffies",
@@ -137,6 +169,8 @@ static const struct ctl_table test_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_ms_jiffies,
+		.min		= INT_MIN,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "int_userhz_jiffies",
@@ -144,6 +178,8 @@ static const struct ctl_table test_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_userhz_jiffies,
+		.min		= INT_MIN,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "uint_0001",
@@ -151,6 +187,8 @@ static const struct ctl_table test_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec,
+		.min		= 0,
+		.max		= UINT_MAX,
 	},
 	{
 		.procname	= "string_0001",
@@ -166,28 +204,73 @@ static const struct ctl_table test_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_do_large_bitmap,
 	},
+	{
+		.procname	= "dynamic_limit",
+		.data		= &test_data.dynamic.limit,
+		.maxlen		= sizeof(test_data.dynamic.limit),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec,
+		.min		= INT_MIN,
+		.max		= INT_MAX,
+	},
+	{
+		.procname	= "dynamic_value",
+		.data		= &test_data.dynamic,
+		.maxlen		= sizeof(test_data.dynamic),
+		.mode		= 0644,
+		.proc_handler	= test_sysctl_dynamic_value,
+		.min		= 0,
+		.max		= ULONG_MAX,
+	},
+	{
+		.procname	= "int_zero",
+		.data		= &test_data.int_zero,
+		.maxlen		= sizeof(test_data.int_zero),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.min		= 0,
+		.max		= 0,
+	},
+	{
+		.procname	= "ulong_zero",
+		.data		= &test_data.ulong_zero,
+		.maxlen		= sizeof(test_data.ulong_zero),
+		.mode		= 0644,
+		.proc_handler	= proc_doulongvec_minmax,
+		.min		= 0,
+		.max		= 0,
+	},
+	{
+		.procname	= "u8_zero",
+		.data		= &test_data.u8_zero,
+		.maxlen		= sizeof(test_data.u8_zero),
+		.mode		= 0644,
+		.proc_handler	= proc_dou8vec_minmax,
+		.min		= 0,
+		.max		= 0,
+	},
 };
 
 static void test_sysctl_calc_match_int_ok(void)
 {
 	int i;
 
-	struct {
+	const struct {
 		int defined;
 		int wanted;
 	} match_int[] = {
-		{.defined = *(int *)SYSCTL_ZERO,	.wanted = 0},
-		{.defined = *(int *)SYSCTL_ONE,		.wanted = 1},
-		{.defined = *(int *)SYSCTL_TWO,		.wanted = 2},
-		{.defined = *(int *)SYSCTL_THREE,	.wanted = 3},
-		{.defined = *(int *)SYSCTL_FOUR,	.wanted = 4},
-		{.defined = *(int *)SYSCTL_ONE_HUNDRED, .wanted = 100},
-		{.defined = *(int *)SYSCTL_TWO_HUNDRED,	.wanted = 200},
-		{.defined = *(int *)SYSCTL_ONE_THOUSAND, .wanted = 1000},
-		{.defined = *(int *)SYSCTL_THREE_THOUSAND, .wanted = 3000},
-		{.defined = *(int *)SYSCTL_INT_MAX,	.wanted = INT_MAX},
-		{.defined = *(int *)SYSCTL_MAXOLDUID,	.wanted = 65535},
-		{.defined = *(int *)SYSCTL_NEG_ONE,	.wanted = -1},
+		{ .defined = 0, .wanted = 0 },
+		{ .defined = 1, .wanted = 1 },
+		{ .defined = 2, .wanted = 2 },
+		{ .defined = 3, .wanted = 3 },
+		{ .defined = 4, .wanted = 4 },
+		{ .defined = 100, .wanted = 100 },
+		{ .defined = 200, .wanted = 200 },
+		{ .defined = 1000, .wanted = 1000 },
+		{ .defined = 3000, .wanted = 3000 },
+		{ .defined = INT_MAX, .wanted = INT_MAX },
+		{ .defined = 65535, .wanted = 65535 },
+		{ .defined = -1, .wanted = -1 },
 	};
 
 	for (i = 0; i < ARRAY_SIZE(match_int); i++)
@@ -282,8 +365,8 @@ static const struct ctl_table table_u8_over[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_FOUR,
-		.extra2		= SYSCTL_ONE_THOUSAND,
+		.min		= 4,
+		.max		= 1000,
 	},
 };
 
@@ -294,8 +377,8 @@ static const struct ctl_table table_u8_under[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_NEG_ONE,
-		.extra2		= SYSCTL_ONE_HUNDRED,
+		.min		= ULONG_MAX,
+		.max		= 100,
 	},
 };
 
@@ -306,8 +389,8 @@ static const struct ctl_table table_u8_valid[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO_HUNDRED,
+		.min		= 0,
+		.max		= 200,
 	},
 };
 
-- 
2.25.1


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

* [RFC PATCH 03/39] sysctl: add KUnit coverage for numeric bounds and handlers
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
  2026-10-28 16:34 ` [RFC PATCH 01/39] " wen.yang
  2026-10-28 16:34 ` [RFC PATCH 02/39] test_sysctl: convert bounds to numeric values wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 04/39] sysctl: cover runtime ranges and context lifecycles in KUnit wen.yang
                   ` (35 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Give numeric bounds and the handlers that consume them executable
coverage: full type ranges, one-sided and zero bounds, entries without
backing storage, explicit handlers for arrays and members, and rejection
of writes that would partially commit.

The test file uses a small set of local constructor macros so the cases
stay readable. They are private to the test, not a kernel API, and have
no production users.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 kernel/sysctl-test.c | 634 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 504 insertions(+), 130 deletions(-)

diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
index 92f94ea28957..f18ec901443f 100644
--- a/kernel/sysctl-test.c
+++ b/kernel/sysctl-test.c
@@ -4,39 +4,146 @@
  */
 
 #include <kunit/test.h>
+#include <linux/jiffies.h>
+#include <linux/limits.h>
 #include <linux/sysctl.h>
+#if IS_ENABLED(CONFIG_NET)
+#include <net/net_namespace.h>
+#endif
+
+/*
+ * Constructors for test entries. They are local to this file: they are not
+ * a kernel API and have no production users.
+ */
+
+/*
+ * Local constructors for test entries: variable-first, with the suffix
+ * letters spelling out the arguments. They are not a kernel API.
+ */#define __CTLTBL_ENTRY_INIT(DATA, NAME, MODE, HANDLER, MIN, MAX, MAXLEN) \
+	{ \
+		.procname	= "" NAME "", \
+		.data		= (DATA), \
+		.maxlen		= (MAXLEN), \
+		.mode		= (MODE), \
+		.proc_handler	= (HANDLER), \
+		.poll		= NULL, \
+		.min		= (MIN), \
+		.max		= (MAX), \
+		.priv		= NULL, \
+	}
+
+#define __CTLTBL_ENTRY(VAR, NAME, MODE, HANDLER, MIN, MAX, MAXLEN) \
+	__CTLTBL_ENTRY_INIT(&(VAR), NAME, MODE, HANDLER, MIN, MAX, MAXLEN)
+
+/* No default association: unsupported automatic types fail to compile. */
+#define __CTLTBL_AUTO_HANDLER(VAR) \
+	_Generic((VAR), \
+		 signed int: proc_dointvec, \
+		 unsigned int: proc_douintvec, \
+		 unsigned long: proc_doulongvec_minmax)
+
+#define __CTLTBL_AUTO_HANDLER_MINMAX(VAR) \
+	_Generic((VAR), \
+		 signed int: proc_dointvec_minmax, \
+		 unsigned int: proc_douintvec_minmax, \
+		 unsigned long: proc_doulongvec_minmax)
+
+/* Bounds do not evaluate VAR, including in explicit-handler forms. */
+#define __CTLTBL_MIN(VAR) \
+	_Generic((VAR), signed int: INT_MIN, default : 0)
+
+#define __CTLTBL_MAX(VAR) \
+	_Generic((VAR), signed int: INT_MAX, unsigned int: UINT_MAX, \
+		 unsigned long: ULONG_MAX, default : ULONG_MAX)
+
+#define CTLTBL_ENTRY_V(VAR) \
+	__CTLTBL_ENTRY(VAR, #VAR, 0444, __CTLTBL_AUTO_HANDLER(VAR), \
+		       __CTLTBL_MIN(VAR), __CTLTBL_MAX(VAR), sizeof(VAR))
+
+#define CTLTBL_ENTRY_VN(VAR, NAME) \
+	__CTLTBL_ENTRY(VAR, NAME, 0444, __CTLTBL_AUTO_HANDLER(VAR), \
+		       __CTLTBL_MIN(VAR), __CTLTBL_MAX(VAR), sizeof(VAR))
+
+#define CTLTBL_ENTRY_VNM(VAR, NAME, MODE) \
+	__CTLTBL_ENTRY(VAR, NAME, MODE, __CTLTBL_AUTO_HANDLER(VAR), \
+		       __CTLTBL_MIN(VAR), __CTLTBL_MAX(VAR), sizeof(VAR))
+
+#define CTLTBL_ENTRY_VNMH(VAR, NAME, MODE, HANDLER) \
+	__CTLTBL_ENTRY(VAR, NAME, MODE, HANDLER, \
+		       __CTLTBL_MIN(VAR), __CTLTBL_MAX(VAR), sizeof(VAR))
+
+#define CTLTBL_ENTRY_VNMR(VAR, NAME, MODE, MIN, MAX) \
+	__CTLTBL_ENTRY(VAR, NAME, MODE, __CTLTBL_AUTO_HANDLER_MINMAX(VAR), \
+		       MIN, MAX, sizeof(VAR))
+
+#define CTLTBL_ENTRY_VNMHR(VAR, NAME, MODE, HANDLER, MIN, MAX) \
+	__CTLTBL_ENTRY(VAR, NAME, MODE, HANDLER, MIN, MAX, sizeof(VAR))
+
+#define CTLTBL_ENTRY_VNMHRL(VAR, NAME, MODE, HANDLER, MIN, MAX, MAXLEN) \
+	__CTLTBL_ENTRY(VAR, NAME, MODE, HANDLER, MIN, MAX, MAXLEN)
+
+#define CTLTBL_ENTRY_NH(NAME, HANDLER) \
+	__CTLTBL_ENTRY_INIT(NULL, NAME, 0444, HANDLER, 0, ULONG_MAX, 0)
+
+#define CTLTBL_ENTRY_NMH(NAME, MODE, HANDLER) \
+	__CTLTBL_ENTRY_INIT(NULL, NAME, MODE, HANDLER, 0, ULONG_MAX, 0)
+
+#define CTLTBL_ENTRY_NMHL(NAME, MODE, HANDLER, MAXLEN) \
+	__CTLTBL_ENTRY_INIT(NULL, NAME, MODE, HANDLER, 0, ULONG_MAX, MAXLEN)
+
+#define CTLTBL_ENTRY_NMHRL(NAME, MODE, HANDLER, MIN, MAX, MAXLEN) \
+	__CTLTBL_ENTRY_INIT(NULL, NAME, MODE, HANDLER, MIN, MAX, MAXLEN)
+
 
 #define KUNIT_PROC_READ 0
 #define KUNIT_PROC_WRITE 1
 
+/*
+ * Test entries that carry handler context: data stays the real value storage
+ * and priv references the context object, as production handlers do.
+ */
+#define SYSCTL_TEST_CTX(VALUE, NAME, MODE, HANDLER, CTX) \
+	{ \
+		.procname	= (NAME), \
+		.data		= &(VALUE), \
+		.maxlen		= sizeof(VALUE), \
+		.mode		= (MODE), \
+		.proc_handler	= (HANDLER), \
+		.min		= 0, \
+		.max		= ULONG_MAX, \
+		.priv		= &(CTX), \
+	}
+
+#define SYSCTL_TEST_CTX_NODATA(NAME, MODE, HANDLER, CTX) \
+	{ \
+		.procname	= (NAME), \
+		.maxlen		= sizeof(int), \
+		.mode		= (MODE), \
+		.proc_handler	= (HANDLER), \
+		.min		= 0, \
+		.max		= ULONG_MAX, \
+		.priv		= &(CTX), \
+	}
+
 /*
  * Test that proc_dointvec will not try to use a NULL .data field even when the
  * length is non-zero.
  */
 static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
 {
-	struct ctl_table null_data_table = {
-		.procname = "foo",
-		/*
-		 * Here we are testing that proc_dointvec behaves correctly when
-		 * we give it a NULL .data field. Normally this would point to a
-		 * piece of memory where the value would be stored.
-		 */
-		.data		= NULL,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE_HUNDRED,
-	};
 	/*
-	 * proc_dointvec expects a buffer in user space, so we allocate one. We
-	 * also need to cast it to __user so sparse doesn't get mad.
+	 * Here we are testing that proc_dointvec behaves correctly when
+	 * we give it a NULL .data field. Normally this would point to a
+	 * piece of memory where the value would be stored.
 	 */
-	void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
-							   GFP_USER);
+	const struct ctl_table null_data_table = CTLTBL_ENTRY_NMHRL("foo", 0644,
+			proc_dointvec, 0, 100, sizeof(int));
+	/*
+	 * proc handlers consume kernel buffers; no userspace access is needed.
+	 */
+	void *buffer = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
 	size_t len;
-	loff_t pos;
+	loff_t pos = 0;
 
 	/*
 	 * We don't care what the starting length is since proc_dointvec should
@@ -66,23 +173,16 @@ static void sysctl_test_api_dointvec_null_tbl_data(struct kunit *test)
 static void sysctl_test_api_dointvec_table_maxlen_unset(struct kunit *test)
 {
 	int data = 0;
-	struct ctl_table data_maxlen_unset_table = {
-		.procname = "foo",
-		.data		= &data,
-		/*
-		 * So .data is no longer NULL, but we tell proc_dointvec its
-		 * length is 0, so it still shouldn't try to use it.
-		 */
-		.maxlen		= 0,
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE_HUNDRED,
-	};
-	void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
-							   GFP_USER);
+	/*
+	 * So .data is no longer NULL, but we tell proc_dointvec its
+	 * length is 0, so it still shouldn't try to use it.
+	 */
+	const struct ctl_table data_maxlen_unset_table = CTLTBL_ENTRY_VNMHRL(data,
+			"foo", 0644, proc_dointvec, 0, 100,
+			0);
+	void *buffer = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
 	size_t len;
-	loff_t pos;
+	loff_t pos = 0;
 
 	/*
 	 * As before, we don't care what buffer length is because proc_dointvec
@@ -113,22 +213,14 @@ static void sysctl_test_api_dointvec_table_len_is_zero(struct kunit *test)
 {
 	int data = 0;
 	/* Good table. */
-	struct ctl_table table = {
-		.procname = "foo",
-		.data		= &data,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE_HUNDRED,
-	};
-	void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
-							   GFP_USER);
+	const struct ctl_table table = CTLTBL_ENTRY_VNMHR(data, "foo", 0644,
+			proc_dointvec, 0, 100);
+	void *buffer = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
 	/*
 	 * However, now our read/write buffer has zero length.
 	 */
 	size_t len = 0;
-	loff_t pos;
+	loff_t pos = 0;
 
 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ, buffer,
 					       &len, &pos));
@@ -147,17 +239,9 @@ static void sysctl_test_api_dointvec_table_read_but_position_set(
 {
 	int data = 0;
 	/* Good table. */
-	struct ctl_table table = {
-		.procname = "foo",
-		.data		= &data,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE_HUNDRED,
-	};
-	void __user *buffer = (void __user *)kunit_kzalloc(test, sizeof(int),
-							   GFP_USER);
+	const struct ctl_table table = CTLTBL_ENTRY_VNMHR(data, "foo", 0644,
+			proc_dointvec, 0, 100);
+	void *buffer = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
 	/*
 	 * We don't care about our buffer length because we start off with a
 	 * non-zero file position.
@@ -182,24 +266,16 @@ static void sysctl_test_dointvec_read_happy_single_positive(struct kunit *test)
 {
 	int data = 0;
 	/* Good table. */
-	struct ctl_table table = {
-		.procname = "foo",
-		.data		= &data,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE_HUNDRED,
-	};
+	const struct ctl_table table = CTLTBL_ENTRY_VNMHR(data, "foo", 0644,
+			proc_dointvec, 0, 100);
 	size_t len = 4;
 	loff_t pos = 0;
-	char *buffer = kunit_kzalloc(test, len, GFP_USER);
-	char __user *user_buffer = (char __user *)buffer;
+	char *buffer = kunit_kzalloc(test, len, GFP_KERNEL);
 	/* Store 13 in the data field. */
 	*((int *)table.data) = 13;
 
 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
-					       user_buffer, &len, &pos));
+					       buffer, &len, &pos));
 	KUNIT_ASSERT_EQ(test, 3, len);
 	buffer[len] = '\0';
 	/* And we read 13 back out. */
@@ -213,23 +289,15 @@ static void sysctl_test_dointvec_read_happy_single_negative(struct kunit *test)
 {
 	int data = 0;
 	/* Good table. */
-	struct ctl_table table = {
-		.procname = "foo",
-		.data		= &data,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE_HUNDRED,
-	};
+	const struct ctl_table table = CTLTBL_ENTRY_VNMHR(data, "foo", 0644,
+			proc_dointvec, 0, 100);
 	size_t len = 5;
 	loff_t pos = 0;
-	char *buffer = kunit_kzalloc(test, len, GFP_USER);
-	char __user *user_buffer = (char __user *)buffer;
+	char *buffer = kunit_kzalloc(test, len, GFP_KERNEL);
 	*((int *)table.data) = -16;
 
 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_READ,
-					       user_buffer, &len, &pos));
+					       buffer, &len, &pos));
 	KUNIT_ASSERT_EQ(test, 4, len);
 	buffer[len] = '\0';
 	KUNIT_EXPECT_STREQ(test, "-16\n", buffer);
@@ -242,25 +310,17 @@ static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
 {
 	int data = 0;
 	/* Good table. */
-	struct ctl_table table = {
-		.procname = "foo",
-		.data		= &data,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE_HUNDRED,
-	};
+	const struct ctl_table table = CTLTBL_ENTRY_VNMHR(data, "foo", 0644,
+			proc_dointvec, 0, 100);
 	char input[] = "9";
 	size_t len = sizeof(input) - 1;
 	loff_t pos = 0;
-	char *buffer = kunit_kzalloc(test, len, GFP_USER);
-	char __user *user_buffer = (char __user *)buffer;
+	char *buffer = kunit_kzalloc(test, len, GFP_KERNEL);
 
 	memcpy(buffer, input, len);
 
 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
-					       user_buffer, &len, &pos));
+					       buffer, &len, &pos));
 	KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
 	KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos);
 	KUNIT_EXPECT_EQ(test, 9, *((int *)table.data));
@@ -272,25 +332,17 @@ static void sysctl_test_dointvec_write_happy_single_positive(struct kunit *test)
 static void sysctl_test_dointvec_write_happy_single_negative(struct kunit *test)
 {
 	int data = 0;
-	struct ctl_table table = {
-		.procname = "foo",
-		.data		= &data,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE_HUNDRED,
-	};
+	const struct ctl_table table = CTLTBL_ENTRY_VNMHR(data, "foo", 0644,
+			proc_dointvec, 0, 100);
 	char input[] = "-9";
 	size_t len = sizeof(input) - 1;
 	loff_t pos = 0;
-	char *buffer = kunit_kzalloc(test, len, GFP_USER);
-	char __user *user_buffer = (char __user *)buffer;
+	char *buffer = kunit_kzalloc(test, len, GFP_KERNEL);
 
 	memcpy(buffer, input, len);
 
 	KUNIT_EXPECT_EQ(test, 0, proc_dointvec(&table, KUNIT_PROC_WRITE,
-					       user_buffer, &len, &pos));
+					       buffer, &len, &pos));
 	KUNIT_EXPECT_EQ(test, sizeof(input) - 1, len);
 	KUNIT_EXPECT_EQ(test, sizeof(input) - 1, pos);
 	KUNIT_EXPECT_EQ(test, -9, *((int *)table.data));
@@ -304,19 +356,11 @@ static void sysctl_test_api_dointvec_write_single_less_int_min(
 		struct kunit *test)
 {
 	int data = 0;
-	struct ctl_table table = {
-		.procname = "foo",
-		.data		= &data,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE_HUNDRED,
-	};
+	const struct ctl_table table = CTLTBL_ENTRY_VNMHR(data, "foo", 0644,
+			proc_dointvec, 0, 100);
 	size_t max_len = 32, len = max_len;
 	loff_t pos = 0;
-	char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
-	char __user *user_buffer = (char __user *)buffer;
+	char *buffer = kunit_kzalloc(test, max_len, GFP_KERNEL);
 	unsigned long abs_of_less_than_min = (unsigned long)INT_MAX
 					     - (INT_MAX + INT_MIN) + 1;
 
@@ -330,7 +374,7 @@ static void sysctl_test_api_dointvec_write_single_less_int_min(
 			max_len);
 
 	KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
-						     user_buffer, &len, &pos));
+						     buffer, &len, &pos));
 	KUNIT_EXPECT_EQ(test, max_len, len);
 	KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
 }
@@ -342,19 +386,11 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max(
 		struct kunit *test)
 {
 	int data = 0;
-	struct ctl_table table = {
-		.procname = "foo",
-		.data		= &data,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
-		.extra1		= SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE_HUNDRED,
-	};
+	const struct ctl_table table = CTLTBL_ENTRY_VNMHR(data, "foo", 0644,
+			proc_dointvec, 0, 100);
 	size_t max_len = 32, len = max_len;
 	loff_t pos = 0;
-	char *buffer = kunit_kzalloc(test, max_len, GFP_USER);
-	char __user *user_buffer = (char __user *)buffer;
+	char *buffer = kunit_kzalloc(test, max_len, GFP_KERNEL);
 	unsigned long greater_than_max = (unsigned long)INT_MAX + 1;
 
 	KUNIT_ASSERT_GT(test, greater_than_max, (unsigned long)INT_MAX);
@@ -362,11 +398,340 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max(
 					       greater_than_max),
 			max_len);
 	KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec(&table, KUNIT_PROC_WRITE,
-						     user_buffer, &len, &pos));
+						     buffer, &len, &pos));
 	KUNIT_ASSERT_EQ(test, max_len, len);
 	KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
 }
 
+/*
+ * priv is separate from data and is never interpreted by the core. Macro
+ * entries leave it NULL; a custom entry can attach typed context without
+ * displacing the value storage that standard handlers use.
+ */
+/* Invoke the selected handler directly, with kernel buffers and a fresh position. */
+static void sysctl_test_entry_roundtrip(struct kunit *test,
+					const struct ctl_table *table, const char *input)
+{
+	char expected[32];
+	char *buffer = kunit_kzalloc(test, sizeof(expected), GFP_KERNEL);
+	size_t len = strlen(input);
+	loff_t pos = 0;
+
+	KUNIT_ASSERT_NOT_NULL(test, buffer);
+	KUNIT_ASSERT_LT(test, len, sizeof(expected) - 1);
+	memcpy(buffer, input, len);
+	KUNIT_ASSERT_EQ(test, table->proc_handler(table, KUNIT_PROC_WRITE,
+						  buffer, &len, &pos), 0);
+	KUNIT_EXPECT_EQ(test, len, strlen(input));
+	KUNIT_EXPECT_EQ(test, pos, strlen(input));
+
+	snprintf(expected, sizeof(expected), "%s\n", input);
+	memset(buffer, 0, sizeof(expected));
+	len = sizeof(expected) - 1;
+	pos = 0;
+	KUNIT_ASSERT_EQ(test, table->proc_handler(table, KUNIT_PROC_READ,
+						  buffer, &len, &pos), 0);
+	KUNIT_ASSERT_LT(test, len, sizeof(expected));
+	buffer[len] = '\0';
+	KUNIT_EXPECT_EQ(test, len, strlen(expected));
+	KUNIT_EXPECT_STREQ(test, buffer, expected);
+}
+
+static int sysctl_test_owned_data;
+
+static int sysctl_test_owned_handler(const struct ctl_table *table, int dir,
+				     void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table resolved = *table;
+
+	resolved.data = &sysctl_test_owned_data;
+	resolved.maxlen = sizeof(sysctl_test_owned_data);
+	return proc_dointvec(&resolved, dir, buffer, lenp, ppos);
+}
+
+static void sysctl_test_entry_null_handler(struct kunit *test)
+{
+	const struct ctl_table table = CTLTBL_ENTRY_NH("owned", sysctl_test_owned_handler);
+
+	sysctl_test_owned_data = 0;
+	KUNIT_EXPECT_PTR_EQ(test, table.data, NULL);
+	KUNIT_EXPECT_EQ(test, table.maxlen, 0);
+	KUNIT_EXPECT_EQ(test, table.mode, 0444);
+	KUNIT_EXPECT_EQ(test, table.min, 0UL);
+	KUNIT_EXPECT_EQ(test, table.max, ULONG_MAX);
+	sysctl_test_entry_roundtrip(test, &table, "31");
+	KUNIT_EXPECT_EQ(test, sysctl_test_owned_data, 31);
+	sysctl_test_owned_data = 0;
+}
+
+static struct {
+	int vector[2];
+	int member;
+	char string[8];
+} sysctl_test_static_data;
+
+/* Both automatic and explicit-handler forms must allow static initializers. */
+static const struct ctl_table sysctl_test_static_entries[] = {
+	CTLTBL_ENTRY_VNMH(sysctl_test_static_data.vector, "vector", 0644, proc_dointvec),
+	CTLTBL_ENTRY_VNMH(sysctl_test_static_data.member, "member", 0644, proc_dointvec_minmax),
+	CTLTBL_ENTRY_VNMH(sysctl_test_static_data.string, "string", 0644, proc_dostring),
+	CTLTBL_ENTRY_VNMHRL(sysctl_test_static_data.vector, "first", 0644,
+			    proc_dointvec, INT_MIN, INT_MAX, sizeof(int)),
+	CTLTBL_ENTRY_VNM(sysctl_test_static_data.member, "automatic", 0644),
+};
+
+static void sysctl_test_entry_explicit_handler(struct kunit *test)
+{
+	const struct ctl_table *entries = sysctl_test_static_entries;
+
+	memset(&sysctl_test_static_data, 0, sizeof(sysctl_test_static_data));
+	KUNIT_EXPECT_PTR_EQ(test, entries[0].data, &sysctl_test_static_data.vector);
+	KUNIT_EXPECT_EQ(test, entries[0].maxlen, sizeof(sysctl_test_static_data.vector));
+	KUNIT_EXPECT_PTR_EQ(test, entries[1].data, &sysctl_test_static_data.member);
+	KUNIT_EXPECT_EQ(test, entries[1].maxlen, sizeof(sysctl_test_static_data.member));
+	KUNIT_EXPECT_TRUE(test, entries[1].proc_handler == proc_dointvec_minmax);
+	KUNIT_EXPECT_PTR_EQ(test, entries[2].data, &sysctl_test_static_data.string);
+	KUNIT_EXPECT_EQ(test, entries[2].maxlen, sizeof(sysctl_test_static_data.string));
+	KUNIT_EXPECT_TRUE(test, entries[2].proc_handler == proc_dostring);
+	KUNIT_EXPECT_EQ(test, entries[3].maxlen, sizeof(int));
+	KUNIT_EXPECT_TRUE(test, entries[4].proc_handler == proc_dointvec);
+	KUNIT_EXPECT_EQ(test, entries[0].min, 0UL);
+	KUNIT_EXPECT_EQ(test, entries[0].max, ULONG_MAX);
+	KUNIT_EXPECT_EQ(test, entries[1].min, (unsigned long)INT_MIN);
+	KUNIT_EXPECT_EQ(test, entries[1].max, (unsigned long)INT_MAX);
+	KUNIT_EXPECT_EQ(test, entries[2].min, 0UL);
+	KUNIT_EXPECT_EQ(test, entries[2].max, ULONG_MAX);
+	KUNIT_EXPECT_EQ(test, entries[3].min, (unsigned long)INT_MIN);
+	KUNIT_EXPECT_EQ(test, entries[3].max, (unsigned long)INT_MAX);
+
+	sysctl_test_entry_roundtrip(test, &entries[0], "7\t8");
+	KUNIT_EXPECT_EQ(test, sysctl_test_static_data.vector[0], 7);
+	KUNIT_EXPECT_EQ(test, sysctl_test_static_data.vector[1], 8);
+	sysctl_test_entry_roundtrip(test, &entries[1], "-9");
+	KUNIT_EXPECT_EQ(test, sysctl_test_static_data.member, -9);
+	sysctl_test_entry_roundtrip(test, &entries[2], "hello");
+	KUNIT_EXPECT_STREQ(test, sysctl_test_static_data.string, "hello");
+	/* An explicit length limits the write without changing sizeof(vector). */
+	sysctl_test_entry_roundtrip(test, &entries[3], "11");
+	KUNIT_EXPECT_EQ(test, sysctl_test_static_data.vector[0], 11);
+	KUNIT_EXPECT_EQ(test, sysctl_test_static_data.vector[1], 8);
+	sysctl_test_entry_roundtrip(test, &entries[4], "-16");
+	KUNIT_EXPECT_EQ(test, sysctl_test_static_data.member, -16);
+}
+
+/* A rejected write must not change any byte of the backing storage. */
+static void sysctl_test_entry_reject(struct kunit *test,
+				     const struct ctl_table *table, const char *input)
+{
+	void *saved = kunit_kmalloc(test, table->maxlen, GFP_KERNEL);
+	char *buffer = kunit_kstrdup(test, input, GFP_KERNEL);
+	size_t len = strlen(input);
+	loff_t pos = 0;
+
+	KUNIT_ASSERT_NOT_NULL(test, saved);
+	KUNIT_ASSERT_NOT_NULL(test, buffer);
+	memcpy(saved, table->data, table->maxlen);
+	KUNIT_EXPECT_EQ(test, table->proc_handler(table, KUNIT_PROC_WRITE,
+						  buffer, &len, &pos), -EINVAL);
+	KUNIT_EXPECT_MEMEQ(test, table->data, saved, table->maxlen);
+}
+
+/* Entries are full-range, lower-only, upper-only and explicitly zero-only. */
+static void sysctl_test_entry_range_limits(struct kunit *test,
+					   const struct ctl_table *entries,
+					   const char *minimum, const char *maximum,
+					   const char *underflow, const char *overflow)
+{
+	sysctl_test_entry_roundtrip(test, &entries[0], minimum);
+	sysctl_test_entry_roundtrip(test, &entries[0], maximum);
+	sysctl_test_entry_reject(test, &entries[0], underflow);
+	sysctl_test_entry_reject(test, &entries[0], overflow);
+
+	sysctl_test_entry_roundtrip(test, &entries[1], "2");
+	sysctl_test_entry_roundtrip(test, &entries[1], maximum);
+	sysctl_test_entry_reject(test, &entries[1], "1");
+	sysctl_test_entry_reject(test, &entries[1], overflow);
+
+	sysctl_test_entry_roundtrip(test, &entries[2], minimum);
+	sysctl_test_entry_roundtrip(test, &entries[2], "4");
+	sysctl_test_entry_reject(test, &entries[2], "5");
+	sysctl_test_entry_reject(test, &entries[2], underflow);
+
+	sysctl_test_entry_roundtrip(test, &entries[3], "0");
+	sysctl_test_entry_reject(test, &entries[3], "1");
+	sysctl_test_entry_reject(test, &entries[3], "-1");
+}
+
+static void sysctl_test_entry_int_limits(struct kunit *test)
+{
+	int data = 0;
+	const struct ctl_table entries[] = {
+		CTLTBL_ENTRY_VNMH(data, "full", 0644, proc_dointvec_minmax),
+		CTLTBL_ENTRY_VNMR(data, "lower", 0644, 2, INT_MAX),
+		CTLTBL_ENTRY_VNMR(data, "upper", 0644, INT_MIN, 4),
+		CTLTBL_ENTRY_VNMR(data, "zero", 0644, 0, 0),
+	};
+	char minimum[32], maximum[32], underflow[32], overflow[32];
+
+	snprintf(minimum, sizeof(minimum), "%d", INT_MIN);
+	snprintf(maximum, sizeof(maximum), "%d", INT_MAX);
+	snprintf(underflow, sizeof(underflow), "-%lu", (unsigned long)INT_MAX + 2);
+	snprintf(overflow, sizeof(overflow), "%lu", (unsigned long)INT_MAX + 1);
+	KUNIT_EXPECT_EQ(test, entries[0].min, (unsigned long)INT_MIN);
+	KUNIT_EXPECT_EQ(test, entries[0].max, (unsigned long)INT_MAX);
+	sysctl_test_entry_range_limits(test, entries, minimum, maximum, underflow, overflow);
+	KUNIT_EXPECT_EQ(test, data, 0);
+	sysctl_test_entry_roundtrip(test, &entries[0], minimum);
+	KUNIT_EXPECT_EQ(test, data, INT_MIN);
+	sysctl_test_entry_roundtrip(test, &entries[0], maximum);
+	KUNIT_EXPECT_EQ(test, data, INT_MAX);
+}
+
+static void sysctl_test_entry_ulong_limits(struct kunit *test)
+{
+	unsigned long data = 0;
+	const struct ctl_table entries[] = {
+		CTLTBL_ENTRY_VNMH(data, "full", 0644, proc_doulongvec_minmax),
+		CTLTBL_ENTRY_VNMR(data, "lower", 0644, 2, ULONG_MAX),
+		CTLTBL_ENTRY_VNMR(data, "upper", 0644, 0, 4),
+		CTLTBL_ENTRY_VNMR(data, "zero", 0644, 0, 0),
+	};
+	char maximum[32], overflow[32];
+
+	snprintf(maximum, sizeof(maximum), "%lu", ULONG_MAX);
+	/* Appending a digit overflows unsigned long on both 32-bit and 64-bit. */
+	snprintf(overflow, sizeof(overflow), "%lu0", ULONG_MAX);
+	KUNIT_EXPECT_EQ(test, entries[0].min, 0UL);
+	KUNIT_EXPECT_EQ(test, entries[0].max, ULONG_MAX);
+	sysctl_test_entry_range_limits(test, entries, "0", maximum, "-1", overflow);
+	KUNIT_EXPECT_EQ(test, data, 0UL);
+	sysctl_test_entry_roundtrip(test, &entries[0], maximum);
+	KUNIT_EXPECT_EQ(test, data, ULONG_MAX);
+}
+
+static void sysctl_test_entry_u8_limits(struct kunit *test)
+{
+	u8 data = 0;
+	const struct ctl_table entries[] = {
+		CTLTBL_ENTRY_VNMHR(data, "full", 0644, proc_dou8vec_minmax, 0, U8_MAX),
+		CTLTBL_ENTRY_VNMHR(data, "lower", 0644, proc_dou8vec_minmax, 2, U8_MAX),
+		CTLTBL_ENTRY_VNMHR(data, "upper", 0644, proc_dou8vec_minmax, 0, 4),
+		CTLTBL_ENTRY_VNMHR(data, "zero", 0644, proc_dou8vec_minmax, 0, 0),
+		CTLTBL_ENTRY_VNMHR(data, "both", 0644, proc_dou8vec_minmax, 2, 4),
+	};
+	char maximum[32], overflow[32];
+
+	snprintf(maximum, sizeof(maximum), "%u", U8_MAX);
+	snprintf(overflow, sizeof(overflow), "%u", U8_MAX + 1);
+	sysctl_test_entry_range_limits(test, entries, "0", maximum, "-1", overflow);
+	sysctl_test_entry_roundtrip(test, &entries[0], maximum);
+	KUNIT_EXPECT_EQ(test, data, U8_MAX);
+	sysctl_test_entry_roundtrip(test, &entries[4], "2");
+	KUNIT_EXPECT_EQ(test, data, 2);
+	sysctl_test_entry_roundtrip(test, &entries[4], "4");
+	KUNIT_EXPECT_EQ(test, data, 4);
+	sysctl_test_entry_reject(test, &entries[4], "1");
+	sysctl_test_entry_reject(test, &entries[4], "5");
+}
+
+static void sysctl_test_entry_array_no_partial_commit(struct kunit *test)
+{
+	int signed_data[] = { 2, 3 };
+	unsigned long long_data[] = { 2, 3 };
+	const struct ctl_table entries[] = {
+		CTLTBL_ENTRY_VNMHR(signed_data, "signed", 0644, proc_dointvec_minmax, 2, 4),
+		CTLTBL_ENTRY_VNMHR(long_data, "long", 0644, proc_doulongvec_minmax, 2, 4),
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(entries); i++) {
+		sysctl_test_entry_reject(test, &entries[i], "4\t5");
+		sysctl_test_entry_reject(test, &entries[i], "4\t1");
+		sysctl_test_entry_reject(test, &entries[i], "4\tbad");
+		sysctl_test_entry_reject(test, &entries[i], "4\t-1");
+		sysctl_test_entry_roundtrip(test, &entries[i], "4\t2");
+	}
+	KUNIT_EXPECT_EQ(test, signed_data[0], 4);
+	KUNIT_EXPECT_EQ(test, signed_data[1], 2);
+	KUNIT_EXPECT_EQ(test, long_data[0], 4UL);
+	KUNIT_EXPECT_EQ(test, long_data[1], 2UL);
+}
+
+/* Unsigned int and u8 handlers deliberately do not support arrays. */
+static void sysctl_test_entry_unsigned_arrays_rejected(struct kunit *test)
+{
+	unsigned int unsigned_data[] = { 2, 3 };
+	u8 byte_data[] = { 2, 3 };
+	const struct ctl_table entries[] = {
+		CTLTBL_ENTRY_VNMHR(unsigned_data, "unsigned", 0644,
+				   proc_douintvec_minmax, 0, UINT_MAX),
+		CTLTBL_ENTRY_VNMHR(byte_data, "byte", 0644, proc_dou8vec_minmax, 0, U8_MAX),
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(entries); i++) {
+		char buffer[32] = {};
+		size_t len = sizeof(buffer);
+		loff_t pos = 0;
+
+		sysctl_test_entry_reject(test, &entries[i], "4\t2");
+		KUNIT_EXPECT_EQ(test, entries[i].proc_handler(&entries[i], KUNIT_PROC_READ,
+							      buffer, &len, &pos), -EINVAL);
+	}
+}
+
+/* Each wrapper reads the related bound anew and resolves data in a local copy. */
+/* Context handlers resolve real storage on a local table; data is not a value. */
+/*
+ * Standard handlers only touch data and maxlen. Attaching priv to such an
+ * entry must not change what is stored, so a stray context pointer cannot
+ * redirect a write to the wrong address.
+ */
+/*
+ * Registration refuses priv on a standard handler: the context would be
+ * silently ignored, which is always a configuration mistake.
+ */
+
+/*
+ * A dynamic range wrapper reads its related limit through priv on each call
+ * while data keeps pointing at the value, mirroring addrconf and devinet.
+ */
+/*
+ * Dynamic range families refresh their related limit on every call. These
+ * generic wrappers model the delegation pattern used by scheduler, IPC,
+ * fragment, namespace and jiffies sysctls.
+ */
+/*
+ * Sibling thresholds bound each other. These mirror the fragment and SCTP
+ * handlers that read the other sysctl's current value at every call.
+ */
+
+/* Two object pointers supply value and limit without becoming bounds. */
+/* An owner embedded around the bound storage can be recovered with container_of(). */
+/*
+ * A registered table may be unregistered and recreated. The table and its
+ * handler context are caller-owned; registration must not rewrite either.
+ */
+/*
+ * Registration copies the table entry but must leave data and priv pointing
+ * at the caller-owned objects; the handler resolves storage through data and
+ * context through priv on every call.
+ */
+#if IS_ENABLED(CONFIG_NET) && IS_ENABLED(CONFIG_SYSCTL)
+/*
+ * Simulate per-net table duplication and teardown through the real
+ * register_net_sysctl_sz() path. This does not create a namespace, but it
+ * exercises copied tables, independent contexts, registration failure and
+ * unregister/re-register ordering.
+ */
+#endif
+
+/*
+ * Registered-entry harness. A static handler cannot be called by symbol from
+ * KUnit, but a registered table exposes its proc_handler through the entry.
+ * This helper is deliberately table-agnostic: the demo below uses a synthetic
+ * table, while future tests can pass a header obtained from a real subsystem.
+ */
 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 +743,15 @@ 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_entry_null_handler),
+	KUNIT_CASE(sysctl_test_entry_explicit_handler),
+	KUNIT_CASE(sysctl_test_entry_int_limits),
+	KUNIT_CASE(sysctl_test_entry_ulong_limits),
+	KUNIT_CASE(sysctl_test_entry_u8_limits),
+	KUNIT_CASE(sysctl_test_entry_array_no_partial_commit),
+	KUNIT_CASE(sysctl_test_entry_unsigned_arrays_rejected),
+#if IS_ENABLED(CONFIG_NET) && IS_ENABLED(CONFIG_SYSCTL)
+#endif
 	{}
 };
 
-- 
2.25.1


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

* [RFC PATCH 04/39] sysctl: cover runtime ranges and context lifecycles in KUnit
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (2 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 03/39] sysctl: add KUnit coverage for numeric bounds and handlers wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 05/39] sysctl: cover priv in KUnit and the debug test module wen.yang
                   ` (34 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Exercise per-call refresh for scalar and jiffies range families,
including bounds reached through runtime pointers and sibling sysctls.
Verify typed object context, owner derivation and bool handling.

Simulate per-net table duplication through register_net_sysctl_sz():
independent contexts, registration failure, and unregister/re-register
with a replacement context. Add a registered-entry harness demo that
finds an entry in a registered table and invokes its proc_handler, so
static handlers can be tested without copying their implementation.
Also cover generic registration and invalid numeric bounds.

The KUnit module references both millisecond-jiffies range handlers, so
export the signed one alongside its unsigned long sibling.

Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 kernel/sysctl-test.c  | 651 ++++++++++++++++++++++++++++++++++++++++++
 kernel/time/jiffies.c |   1 +
 2 files changed, 652 insertions(+)

diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
index f18ec901443f..dd658ca18793 100644
--- a/kernel/sysctl-test.c
+++ b/kernel/sysctl-test.c
@@ -682,6 +682,18 @@ static void sysctl_test_entry_unsigned_arrays_rejected(struct kunit *test)
 
 /* Each wrapper reads the related bound anew and resolves data in a local copy. */
 /* Context handlers resolve real storage on a local table; data is not a value. */
+static int sysctl_test_entry_write(struct kunit *test, const struct ctl_table *table,
+				   const char *input)
+{
+	char *buffer = kunit_kstrdup(test, input, GFP_KERNEL);
+	size_t len = strlen(input);
+	loff_t pos = 0;
+
+	if (!buffer)
+		return -ENOMEM;
+	return table->proc_handler(table, KUNIT_PROC_WRITE, buffer, &len, &pos);
+}
+
 /*
  * Standard handlers only touch data and maxlen. Attaching priv to such an
  * entry must not change what is stored, so a stray context pointer cannot
@@ -701,22 +713,464 @@ static void sysctl_test_entry_unsigned_arrays_rejected(struct kunit *test)
  * generic wrappers model the delegation pattern used by scheduler, IPC,
  * fragment, namespace and jiffies sysctls.
  */
+struct sysctl_test_dynamic {
+	union {
+		int i;
+		unsigned int u;
+		unsigned long l;
+		u8 b;
+	} value;
+	unsigned long min, max;
+	unsigned int calls;
+	proc_handler *handler;
+};
+
+static int sysctl_test_dynamic_handler(const struct ctl_table *table, int dir,
+				       void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct sysctl_test_dynamic *ctx = table->priv;
+	struct ctl_table resolved = *table;
+
+	ctx->calls++;
+	resolved.data = &ctx->value;
+	resolved.maxlen = ctx->handler == proc_doulongvec_minmax ?
+			  sizeof(ctx->value.l) :
+			  ctx->handler == proc_dou8vec_minmax ? sizeof(ctx->value.b) :
+			  sizeof(ctx->value.i);
+	resolved.min = ctx->min;
+	resolved.max = ctx->max;
+	return ctx->handler(&resolved, dir, buffer, lenp, ppos);
+}
+
+static unsigned long sysctl_test_dynamic_read(const struct sysctl_test_dynamic *ctx)
+{
+	if (ctx->handler == proc_doulongvec_minmax)
+		return ctx->value.l;
+	if (ctx->handler == proc_douintvec_minmax)
+		return ctx->value.u;
+	if (ctx->handler == proc_dou8vec_minmax)
+		return ctx->value.b;
+	return (unsigned long)(long)ctx->value.i;
+}
+
+static void sysctl_test_dynamic_range(struct kunit *test)
+{
+	static proc_handler *const handlers[] = {
+		proc_dointvec_minmax, proc_douintvec_minmax,
+		proc_doulongvec_minmax, proc_dou8vec_minmax,
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(handlers); i++) {
+		struct sysctl_test_dynamic ctx = { .min = 2, .max = 4,
+						   .handler = handlers[i] };
+		const struct ctl_table table = SYSCTL_TEST_CTX_NODATA("dynamic",
+								     0644,
+								     sysctl_test_dynamic_handler,
+								     ctx);
+		sysctl_test_entry_roundtrip(test, &table, "4");
+		KUNIT_EXPECT_EQ(test, sysctl_test_dynamic_read(&ctx), 4UL);
+		KUNIT_EXPECT_GT(test, ctx.calls, 0U);
+
+		ctx.max = 2;
+		KUNIT_EXPECT_EQ(test, -EINVAL, sysctl_test_entry_write(test, &table, "3"));
+		KUNIT_EXPECT_EQ(test, sysctl_test_dynamic_read(&ctx), 4UL);
+
+		ctx.min = 3;
+		ctx.max = 4;
+		sysctl_test_entry_roundtrip(test, &table, "3");
+		KUNIT_EXPECT_EQ(test, sysctl_test_dynamic_read(&ctx), 3UL);
+		KUNIT_EXPECT_EQ(test, -EINVAL, sysctl_test_entry_write(test, &table, "2"));
+
+		ctx.min = 2;
+		ctx.max = 8;
+		sysctl_test_entry_roundtrip(test, &table, "8");
+		KUNIT_EXPECT_EQ(test, sysctl_test_dynamic_read(&ctx), 8UL);
+		KUNIT_EXPECT_GT(test, ctx.calls, 0U);
+		KUNIT_EXPECT_PTR_EQ(test, table.priv, &ctx);
+		KUNIT_EXPECT_EQ(test, table.min, 0UL);
+		KUNIT_EXPECT_EQ(test, table.max, ULONG_MAX);
+	}
+}
+
+/* Runtime-bound pointers are dereferenced afresh on every handler call. */
+struct sysctl_test_bound_pointer {
+	int value;
+	int *limit;
+};
+
+static int sysctl_test_bound_pointer_handler(const struct ctl_table *table, int dir,
+					     void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct sysctl_test_bound_pointer *ctx = table->priv;
+	struct ctl_table resolved = *table;
+
+	resolved.data = &ctx->value;
+	resolved.maxlen = sizeof(ctx->value);
+	resolved.min = 0;
+	resolved.max = READ_ONCE(*ctx->limit);
+	return proc_dointvec_minmax(&resolved, dir, buffer, lenp, ppos);
+}
+
+static void sysctl_test_runtime_bound_pointer(struct kunit *test)
+{
+	int limit = 4;
+	struct sysctl_test_bound_pointer ctx = { .limit = &limit };
+	const struct ctl_table table = SYSCTL_TEST_CTX(ctx.value, "pointer_bound",
+						       0644, sysctl_test_bound_pointer_handler,
+						       ctx);
+
+	sysctl_test_entry_roundtrip(test, &table, "4");
+	KUNIT_EXPECT_EQ(test, ctx.value, 4);
+	limit = 2;
+	KUNIT_EXPECT_EQ(test, -EINVAL, sysctl_test_entry_write(test, &table, "3"));
+	KUNIT_EXPECT_EQ(test, ctx.value, 4);
+	limit = 8;
+	sysctl_test_entry_roundtrip(test, &table, "8");
+	KUNIT_EXPECT_EQ(test, ctx.value, 8);
+	KUNIT_EXPECT_PTR_EQ(test, table.priv, &ctx);
+	KUNIT_EXPECT_PTR_EQ(test, ctx.limit, &limit);
+}
+
 /*
  * Sibling thresholds bound each other. These mirror the fragment and SCTP
  * handlers that read the other sysctl's current value at every call.
  */
+struct sysctl_test_sibling_long {
+	unsigned long high;
+	unsigned long low;
+};
+
+static int sysctl_test_high_long_handler(const struct ctl_table *table, int dir,
+					 void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct sysctl_test_sibling_long *ctx = table->priv;
+	struct ctl_table resolved = *table;
+
+	resolved.data = &ctx->high;
+	resolved.maxlen = sizeof(ctx->high);
+	resolved.min = ctx->low;
+	resolved.max = ULONG_MAX;
+	return proc_doulongvec_minmax(&resolved, dir, buffer, lenp, ppos);
+}
+
+static int sysctl_test_low_long_handler(const struct ctl_table *table, int dir,
+					void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct sysctl_test_sibling_long *ctx = table->priv;
+	struct ctl_table resolved = *table;
+
+	resolved.data = &ctx->low;
+	resolved.maxlen = sizeof(ctx->low);
+	resolved.min = 0;
+	resolved.max = ctx->high;
+	return proc_doulongvec_minmax(&resolved, dir, buffer, lenp, ppos);
+}
+
+static void sysctl_test_runtime_sibling_bounds(struct kunit *test)
+{
+	struct sysctl_test_sibling_long ctx = { .high = 4, .low = 2 };
+	const struct ctl_table entries[] = {
+		SYSCTL_TEST_CTX(ctx.high, "high", 0644,
+				sysctl_test_high_long_handler, ctx),
+		SYSCTL_TEST_CTX(ctx.low, "low", 0644,
+				sysctl_test_low_long_handler, ctx),
+	};
+
+	sysctl_test_entry_roundtrip(test, &entries[0], "4");
+	KUNIT_EXPECT_EQ(test, -EINVAL, sysctl_test_entry_write(test, &entries[0], "1"));
+	KUNIT_EXPECT_EQ(test, ctx.high, 4UL);
+	sysctl_test_entry_roundtrip(test, &entries[1], "3");
+	KUNIT_EXPECT_EQ(test, ctx.low, 3UL);
+	KUNIT_EXPECT_EQ(test, -EINVAL, sysctl_test_entry_write(test, &entries[1], "5"));
+	KUNIT_EXPECT_EQ(test, ctx.low, 3UL);
+
+	ctx.low = 1;
+	sysctl_test_entry_roundtrip(test, &entries[0], "2");
+	KUNIT_EXPECT_EQ(test, ctx.high, 2UL);
+	KUNIT_EXPECT_EQ(test, -EINVAL, sysctl_test_entry_write(test, &entries[1], "3"));
+	KUNIT_EXPECT_EQ(test, ctx.low, 1UL);
+	sysctl_test_entry_roundtrip(test, &entries[1], "2");
+	KUNIT_EXPECT_EQ(test, ctx.low, 2UL);
+	KUNIT_EXPECT_PTR_EQ(test, entries[0].priv, &ctx);
+	KUNIT_EXPECT_PTR_EQ(test, entries[1].priv, &ctx);
+}
+
 
 /* Two object pointers supply value and limit without becoming bounds. */
+struct sysctl_test_object_state {
+	int value;
+	int limit;
+};
+
+struct sysctl_test_object_holder {
+	struct sysctl_test_object_state *owner;
+	struct sysctl_test_object_state *related;
+};
+
+static int sysctl_test_object_context_handler(const struct ctl_table *table,
+					      int dir, void *buffer,
+					      size_t *lenp, loff_t *ppos)
+{
+	struct sysctl_test_object_holder *holder = table->priv;
+	struct ctl_table resolved = *table;
+
+	resolved.data = &holder->owner->value;
+	resolved.maxlen = sizeof(holder->owner->value);
+	resolved.min = 0;
+	resolved.max = READ_ONCE(holder->related->limit);
+	return proc_dointvec_minmax(&resolved, dir, buffer, lenp, ppos);
+}
+
+static void sysctl_test_runtime_object_context(struct kunit *test)
+{
+	struct sysctl_test_object_state owner = { .limit = 4 };
+	struct sysctl_test_object_state other = { .limit = 4 };
+	struct sysctl_test_object_holder holder = {
+		.owner = &owner,
+		.related = &other,
+	};
+	const struct ctl_table table = SYSCTL_TEST_CTX_NODATA("object_context", 0644,
+							      sysctl_test_object_context_handler,
+							      holder);
+
+	sysctl_test_entry_roundtrip(test, &table, "4");
+	KUNIT_EXPECT_EQ(test, owner.value, 4);
+	KUNIT_EXPECT_EQ(test, -EINVAL, sysctl_test_entry_write(test, &table, "5"));
+	KUNIT_EXPECT_EQ(test, owner.value, 4);
+
+	other.limit = 2;
+	KUNIT_EXPECT_EQ(test, -EINVAL, sysctl_test_entry_write(test, &table, "3"));
+	KUNIT_EXPECT_EQ(test, owner.value, 4);
+	holder.owner = &other;
+	sysctl_test_entry_roundtrip(test, &table, "2");
+	KUNIT_EXPECT_EQ(test, other.value, 2);
+	KUNIT_EXPECT_EQ(test, owner.value, 4);
+	other.limit = 8;
+	sysctl_test_entry_roundtrip(test, &table, "8");
+	KUNIT_EXPECT_EQ(test, other.value, 8);
+	KUNIT_EXPECT_PTR_EQ(test, table.priv, &holder);
+	KUNIT_EXPECT_PTR_EQ(test, holder.owner, &other);
+	KUNIT_EXPECT_PTR_EQ(test, holder.related, &other);
+}
+
 /* An owner embedded around the bound storage can be recovered with container_of(). */
+struct sysctl_test_owner {
+	int value;
+	int limit;
+};
+
+struct sysctl_test_holder {
+	struct sysctl_test_owner owner;
+};
+
+static int sysctl_test_owner_handler(const struct ctl_table *table, int dir,
+				     void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct sysctl_test_holder *holder =
+		container_of(table->data, struct sysctl_test_holder,
+			     owner.value);
+	struct sysctl_test_owner *owner = &holder->owner;
+	struct ctl_table tmp = *table;
+
+	tmp.data = &owner->value;
+	tmp.maxlen = sizeof(owner->value);
+	tmp.min = 0;
+	tmp.max = READ_ONCE(owner->limit);
+	return proc_dointvec_minmax(&tmp, dir, buffer, lenp, ppos);
+}
+
+static void sysctl_test_runtime_owner_from_data(struct kunit *test)
+{
+	struct sysctl_test_holder holder = { .owner.limit = 4 };
+	const struct ctl_table table = CTLTBL_ENTRY_VNMH(holder.owner.value, "owner",
+							 0644, sysctl_test_owner_handler);
+
+	sysctl_test_entry_roundtrip(test, &table, "4");
+	KUNIT_EXPECT_EQ(test, holder.owner.value, 4);
+	holder.owner.limit = 2;
+	KUNIT_EXPECT_EQ(test, -EINVAL,
+			sysctl_test_entry_write(test, &table, "3"));
+	KUNIT_EXPECT_EQ(test, holder.owner.value, 4);
+	holder.owner.limit = 8;
+	sysctl_test_entry_roundtrip(test, &table, "8");
+	KUNIT_EXPECT_EQ(test, holder.owner.value, 8);
+	KUNIT_EXPECT_PTR_EQ(test, table.data, &holder.owner.value);
+}
+
+static void sysctl_test_entry_bool(struct kunit *test)
+{
+	bool value = false;
+	const struct ctl_table table = CTLTBL_ENTRY_VNMH(value, "bool", 0644,
+							 proc_dobool);
+
+	sysctl_test_entry_roundtrip(test, &table, "1");
+	KUNIT_EXPECT_TRUE(test, value);
+	sysctl_test_entry_roundtrip(test, &table, "0");
+	KUNIT_EXPECT_FALSE(test, value);
+	KUNIT_EXPECT_EQ(test, table.maxlen, sizeof(value));
+	KUNIT_EXPECT_PTR_EQ(test, table.data, &value);
+}
+
+struct sysctl_test_dynamic_jiffies {
+	int int_value;
+	unsigned long ulong_value;
+	int int_min;
+	int int_max;
+	unsigned long ulong_min;
+	unsigned long ulong_max;
+	unsigned int calls;
+};
+
+static int sysctl_test_dynamic_jiffies_int_handler(const struct ctl_table *table,
+						   int dir, void *buffer,
+						   size_t *lenp, loff_t *ppos)
+{
+	struct sysctl_test_dynamic_jiffies *ctx = table->priv;
+	struct ctl_table resolved = *table;
+
+	ctx->calls++;
+	resolved.data = &ctx->int_value;
+	resolved.maxlen = sizeof(ctx->int_value);
+	resolved.min = (unsigned long)(long)ctx->int_min;
+	resolved.max = (unsigned long)(long)ctx->int_max;
+	return proc_dointvec_ms_jiffies_minmax(&resolved, dir, buffer, lenp, ppos);
+}
+
+static int sysctl_test_dynamic_jiffies_ulong_handler(const struct ctl_table *table,
+						     int dir, void *buffer,
+						     size_t *lenp, loff_t *ppos)
+{
+	struct sysctl_test_dynamic_jiffies *ctx = table->priv;
+	struct ctl_table resolved = *table;
+
+	ctx->calls++;
+	resolved.data = &ctx->ulong_value;
+	resolved.maxlen = sizeof(ctx->ulong_value);
+	resolved.min = ctx->ulong_min;
+	resolved.max = ctx->ulong_max;
+	return proc_doulongvec_ms_jiffies_minmax(&resolved, dir, buffer, lenp, ppos);
+}
+
+static void sysctl_test_dynamic_jiffies_ranges(struct kunit *test)
+{
+	struct sysctl_test_dynamic_jiffies ctx = {
+		.int_min = HZ,
+		.int_max = 2 * HZ,
+		.ulong_min = HZ,
+		.ulong_max = 2 * HZ,
+	};
+	const struct ctl_table entries[] = {
+		SYSCTL_TEST_CTX(ctx.int_value, "jiffies_int", 0644,
+				sysctl_test_dynamic_jiffies_int_handler, ctx),
+		SYSCTL_TEST_CTX(ctx.ulong_value, "jiffies_ulong", 0644,
+				sysctl_test_dynamic_jiffies_ulong_handler, ctx),
+	};
+
+	sysctl_test_entry_roundtrip(test, &entries[0], "1000");
+	KUNIT_EXPECT_EQ(test, ctx.int_value, HZ);
+	ctx.int_max = HZ;
+	KUNIT_EXPECT_EQ(test, -EINVAL,
+			sysctl_test_entry_write(test, &entries[0], "1500"));
+	KUNIT_EXPECT_EQ(test, ctx.int_value, HZ);
+	ctx.int_min = 2 * HZ;
+	ctx.int_max = 2 * HZ;
+	sysctl_test_entry_roundtrip(test, &entries[0], "2000");
+	KUNIT_EXPECT_EQ(test, ctx.int_value, 2 * HZ);
+
+	sysctl_test_entry_roundtrip(test, &entries[1], "1000");
+	KUNIT_EXPECT_EQ(test, ctx.ulong_value, (unsigned long)HZ);
+	ctx.ulong_max = HZ;
+	KUNIT_EXPECT_EQ(test, -EINVAL,
+			sysctl_test_entry_write(test, &entries[1], "1500"));
+	KUNIT_EXPECT_EQ(test, ctx.ulong_value, (unsigned long)HZ);
+	ctx.ulong_min = 2UL * HZ;
+	ctx.ulong_max = 2UL * HZ;
+	sysctl_test_entry_roundtrip(test, &entries[1], "2000");
+	KUNIT_EXPECT_EQ(test, ctx.ulong_value, 2UL * HZ);
+	KUNIT_EXPECT_GT(test, ctx.calls, 0U);
+	KUNIT_EXPECT_PTR_EQ(test, entries[0].priv, &ctx);
+	KUNIT_EXPECT_PTR_EQ(test, entries[1].priv, &ctx);
+}
+
 /*
  * A registered table may be unregistered and recreated. The table and its
  * handler context are caller-owned; registration must not rewrite either.
  */
+struct sysctl_test_lifecycle {
+	int value;
+	unsigned int calls;
+};
+
+static int sysctl_test_lifecycle_handler(const struct ctl_table *table, int dir,
+					 void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct sysctl_test_lifecycle *ctx = table->priv;
+	struct ctl_table resolved = *table;
+
+	ctx->calls++;
+	resolved.data = &ctx->value;
+	resolved.maxlen = sizeof(ctx->value);
+	resolved.min = (unsigned long)INT_MIN;
+	resolved.max = (unsigned long)INT_MAX;
+	return proc_dointvec_minmax(&resolved, dir, buffer, lenp, ppos);
+}
+
+static void sysctl_test_unregister_table(void *header)
+{
+	unregister_sysctl_table(header);
+}
+
+static void sysctl_test_context_registration_lifecycle(struct kunit *test)
+{
+	struct sysctl_test_lifecycle ctx = {};
+	const struct ctl_table table = SYSCTL_TEST_CTX(ctx.value, "context", 0644,
+						       sysctl_test_lifecycle_handler, ctx);
+	const char *path = "sysctl_kunit_lifecycle";
+	struct ctl_table_header *header;
+
+	header = register_sysctl_sz(path, &table, 1);
+	KUNIT_ASSERT_NOT_NULL(test, header);
+	kunit_add_action(test, sysctl_test_unregister_table, header);
+
+	sysctl_test_entry_roundtrip(test, &table, "31");
+	KUNIT_EXPECT_EQ(test, ctx.value, 31);
+	KUNIT_EXPECT_GT(test, ctx.calls, 0U);
+	KUNIT_EXPECT_PTR_EQ(test, table.priv, &ctx);
+
+	kunit_release_action(test, sysctl_test_unregister_table, header);
+	header = register_sysctl_sz(path, &table, 1);
+	KUNIT_ASSERT_NOT_NULL(test, header);
+	kunit_add_action(test, sysctl_test_unregister_table, header);
+
+	sysctl_test_entry_roundtrip(test, &table, "-16");
+	KUNIT_EXPECT_EQ(test, ctx.value, -16);
+	KUNIT_EXPECT_GT(test, ctx.calls, 2U);
+	KUNIT_EXPECT_PTR_EQ(test, table.priv, &ctx);
+	KUNIT_EXPECT_EQ(test, table.maxlen, sizeof(ctx.value));
+	KUNIT_EXPECT_EQ(test, table.min, 0UL);
+	KUNIT_EXPECT_EQ(test, table.max, ULONG_MAX);
+
+	kunit_release_action(test, sysctl_test_unregister_table, header);
+}
+
 /*
  * Registration copies the table entry but must leave data and priv pointing
  * at the caller-owned objects; the handler resolves storage through data and
  * context through priv on every call.
  */
+/* Registration rejects numeric bounds that cannot represent the element type. */
+static void sysctl_test_context_registration_reject(struct kunit *test)
+{
+	u8 value = 0;
+	const struct ctl_table table = CTLTBL_ENTRY_VNMHR(value, "invalid", 0644,
+							  proc_dou8vec_minmax, 0,
+							  U8_MAX + 1);
+
+	KUNIT_EXPECT_PTR_EQ(test, NULL,
+			    register_sysctl_sz("sysctl_kunit_invalid", &table, 1));
+}
+
 #if IS_ENABLED(CONFIG_NET) && IS_ENABLED(CONFIG_SYSCTL)
 /*
  * Simulate per-net table duplication and teardown through the real
@@ -724,6 +1178,116 @@ static void sysctl_test_entry_unsigned_arrays_rejected(struct kunit *test)
  * exercises copied tables, independent contexts, registration failure and
  * unregister/re-register ordering.
  */
+struct sysctl_test_net_context {
+	int value;
+	int limit;
+	unsigned int calls;
+};
+
+static int sysctl_test_net_handler(const struct ctl_table *table, int dir,
+				   void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct sysctl_test_net_context *ctx = table->priv;
+	struct ctl_table tmp = *table;
+
+	ctx->calls++;
+	tmp.data = &ctx->value;
+	tmp.maxlen = sizeof(ctx->value);
+	tmp.min = 0;
+	tmp.max = READ_ONCE(ctx->limit);
+	return proc_dointvec_minmax(&tmp, dir, buffer, lenp, ppos);
+}
+
+static void sysctl_test_unregister_net_table(void *header)
+{
+	unregister_net_sysctl_table(header);
+}
+
+static void sysctl_test_net_context_isolation(struct kunit *test)
+{
+	struct sysctl_test_net_context first = { .limit = 50 };
+	struct sysctl_test_net_context second = { .limit = 10 };
+	struct ctl_table first_table = SYSCTL_TEST_CTX(first.value, "first", 0644,
+						       sysctl_test_net_handler, first);
+	struct ctl_table second_table = SYSCTL_TEST_CTX(second.value, "second", 0644,
+							sysctl_test_net_handler, second);
+	struct ctl_table_header *header;
+
+	header = register_net_sysctl_sz(&init_net, "net/sysctl_kunit_a",
+					&first_table, 1);
+	KUNIT_ASSERT_NOT_NULL(test, header);
+	kunit_add_action(test, sysctl_test_unregister_net_table, header);
+
+	header = register_net_sysctl_sz(&init_net, "net/sysctl_kunit_b",
+					&second_table, 1);
+	KUNIT_ASSERT_NOT_NULL(test, header);
+	kunit_add_action(test, sysctl_test_unregister_net_table, header);
+
+	sysctl_test_entry_roundtrip(test, &first_table, "40");
+	KUNIT_EXPECT_EQ(test, first.value, 40);
+	KUNIT_EXPECT_EQ(test, second.value, 0);
+	KUNIT_EXPECT_EQ(test, -EINVAL,
+			sysctl_test_entry_write(test, &second_table, "20"));
+	KUNIT_EXPECT_EQ(test, second.value, 0);
+	sysctl_test_entry_roundtrip(test, &second_table, "10");
+	KUNIT_EXPECT_EQ(test, second.value, 10);
+	KUNIT_EXPECT_EQ(test, first.value, 40);
+
+	first.limit = 5;
+	KUNIT_EXPECT_EQ(test, -EINVAL,
+			sysctl_test_entry_write(test, &first_table, "6"));
+	KUNIT_EXPECT_EQ(test, first.value, 40);
+	KUNIT_EXPECT_PTR_EQ(test, first_table.priv, &first);
+	KUNIT_EXPECT_PTR_EQ(test, second_table.priv, &second);
+}
+
+static void sysctl_test_net_context_registration_cycle(struct kunit *test)
+{
+	struct sysctl_test_net_context first = { .limit = 100 };
+	struct sysctl_test_net_context second = { .limit = 50 };
+	struct ctl_table table = SYSCTL_TEST_CTX(first.value, "cycle", 0644,
+						 sysctl_test_net_handler, first);
+	struct ctl_table_header *header;
+
+	header = register_net_sysctl_sz(&init_net, "net/sysctl_kunit_cycle",
+					&table, 1);
+	KUNIT_ASSERT_NOT_NULL(test, header);
+	kunit_add_action(test, sysctl_test_unregister_net_table, header);
+	sysctl_test_entry_roundtrip(test, &table, "31");
+	KUNIT_EXPECT_EQ(test, first.value, 31);
+	kunit_release_action(test, sysctl_test_unregister_net_table, header);
+
+	/* A recreated table may carry a new owner context. */
+	table.data = &second.value;
+	table.maxlen = sizeof(second.value);
+	table.priv = &second;
+	header = register_net_sysctl_sz(&init_net, "net/sysctl_kunit_cycle",
+					&table, 1);
+	KUNIT_ASSERT_NOT_NULL(test, header);
+	kunit_add_action(test, sysctl_test_unregister_net_table, header);
+	sysctl_test_entry_roundtrip(test, &table, "50");
+	KUNIT_EXPECT_EQ(test, second.value, 50);
+	KUNIT_EXPECT_EQ(test, first.value, 31);
+	KUNIT_EXPECT_PTR_EQ(test, table.priv, &second);
+	KUNIT_EXPECT_EQ(test, table.maxlen, sizeof(second.value));
+
+	kunit_release_action(test, sysctl_test_unregister_net_table, header);
+}
+
+static void sysctl_test_net_context_registration_reject(struct kunit *test)
+{
+	u8 value = 0;
+	struct ctl_table table = CTLTBL_ENTRY_VNMHR(value, "invalid", 0644,
+						    proc_dou8vec_minmax, 0,
+						    U8_MAX + 1);
+	struct ctl_table_header *header;
+
+	header = register_net_sysctl_sz(&init_net, "net/sysctl_kunit_invalid",
+					&table, 1);
+	KUNIT_EXPECT_PTR_EQ(test, NULL, header);
+	if (header)
+		unregister_net_sysctl_table(header);
+}
 #endif
 
 /*
@@ -732,6 +1296,80 @@ static void sysctl_test_entry_unsigned_arrays_rejected(struct kunit *test)
  * This helper is deliberately table-agnostic: the demo below uses a synthetic
  * table, while future tests can pass a header obtained from a real subsystem.
  */
+struct sysctl_test_registered {
+	struct ctl_table_header *header;
+	const struct ctl_table *entry;
+};
+
+static const struct ctl_table *sysctl_test_find_entry(const struct ctl_table_header *header,
+						      const char *name)
+{
+	int i;
+
+	for (i = 0; i < header->ctl_table_size; i++) {
+		const struct ctl_table *entry = &header->ctl_table_arg[i];
+
+		if (entry->procname && !strcmp(entry->procname, name))
+			return entry;
+	}
+	return NULL;
+}
+
+static void sysctl_test_unregister_registered(void *header)
+{
+	unregister_sysctl_table(header);
+}
+
+struct sysctl_test_demo_ctx {
+	int value;
+	int limit;
+	unsigned int calls;
+};
+
+static int sysctl_test_demo_handler(const struct ctl_table *table, int dir,
+				    void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct sysctl_test_demo_ctx *ctx = table->priv;
+	struct ctl_table tmp = *table;
+
+	ctx->calls++;
+	tmp.data = &ctx->value;
+	tmp.maxlen = sizeof(ctx->value);
+	tmp.min = 0;
+	tmp.max = READ_ONCE(ctx->limit);
+	return proc_dointvec_minmax(&tmp, dir, buffer, lenp, ppos);
+}
+
+static void sysctl_test_registered_entry_demo(struct kunit *test)
+{
+	struct sysctl_test_demo_ctx ctx = { .limit = 4 };
+	struct ctl_table table = SYSCTL_TEST_CTX(ctx.value, "value", 0644,
+						 sysctl_test_demo_handler, ctx);
+	struct sysctl_test_registered registered;
+	const struct ctl_table *entry;
+
+	registered.header = register_sysctl_sz("sysctl_kunit_demo", &table, 1);
+	KUNIT_ASSERT_NOT_NULL(test, registered.header);
+	kunit_add_action(test, sysctl_test_unregister_registered,
+			 registered.header);
+
+	entry = sysctl_test_find_entry(registered.header, "value");
+	KUNIT_ASSERT_NOT_NULL(test, entry);
+	registered.entry = entry;
+
+	sysctl_test_entry_roundtrip(test, registered.entry, "4");
+	KUNIT_EXPECT_EQ(test, ctx.value, 4);
+	ctx.limit = 2;
+	KUNIT_EXPECT_EQ(test, -EINVAL,
+			sysctl_test_entry_write(test, registered.entry, "3"));
+	KUNIT_EXPECT_EQ(test, ctx.value, 4);
+	ctx.limit = 8;
+	sysctl_test_entry_roundtrip(test, registered.entry, "8");
+	KUNIT_EXPECT_EQ(test, ctx.value, 8);
+	KUNIT_EXPECT_PTR_EQ(test, registered.entry->priv, &ctx);
+	KUNIT_EXPECT_PTR_EQ(test, registered.entry->data, &ctx.value);
+}
+
 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),
@@ -750,7 +1388,20 @@ static struct kunit_case sysctl_test_cases[] = {
 	KUNIT_CASE(sysctl_test_entry_u8_limits),
 	KUNIT_CASE(sysctl_test_entry_array_no_partial_commit),
 	KUNIT_CASE(sysctl_test_entry_unsigned_arrays_rejected),
+	KUNIT_CASE(sysctl_test_dynamic_range),
+	KUNIT_CASE(sysctl_test_runtime_bound_pointer),
+	KUNIT_CASE(sysctl_test_runtime_sibling_bounds),
+	KUNIT_CASE(sysctl_test_runtime_object_context),
+	KUNIT_CASE(sysctl_test_runtime_owner_from_data),
+	KUNIT_CASE(sysctl_test_entry_bool),
+	KUNIT_CASE(sysctl_test_dynamic_jiffies_ranges),
+	KUNIT_CASE(sysctl_test_context_registration_lifecycle),
+	KUNIT_CASE(sysctl_test_context_registration_reject),
+	KUNIT_CASE(sysctl_test_registered_entry_demo),
 #if IS_ENABLED(CONFIG_NET) && IS_ENABLED(CONFIG_SYSCTL)
+	KUNIT_CASE(sysctl_test_net_context_isolation),
+	KUNIT_CASE(sysctl_test_net_context_registration_cycle),
+	KUNIT_CASE(sysctl_test_net_context_registration_reject),
 #endif
 	{}
 };
diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c
index af1c69bf0c23..8dde5a2cc532 100644
--- a/kernel/time/jiffies.c
+++ b/kernel/time/jiffies.c
@@ -316,6 +316,7 @@ int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
 	return proc_dointvec_conv(table, dir, buffer, lenp, ppos,
 				  do_proc_int_conv_ms_jiffies_minmax);
 }
+EXPORT_SYMBOL(proc_dointvec_ms_jiffies_minmax);
 
 /**
  * proc_doulongvec_ms_jiffies_minmax - read a vector of millisecond values with min/max values
-- 
2.25.1


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

* [RFC PATCH 05/39] sysctl: cover priv in KUnit and the debug test module
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (3 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 04/39] sysctl: cover runtime ranges and context lifecycles in KUnit wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 06/39] sysctl: convert core table bounds to numeric values wen.yang
                   ` (33 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Add KUnit cases for priv: a standard handler must ignore it, registration
must reject priv with a standard handler, and a re-registered table must
keep both the value pointer and the context.

Move the debug module's dynamic-value entry to the same shape so it
exercises data-as-value and priv-as-context.

Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 kernel/sysctl-test.c | 72 ++++++++++++++++++++++++++++++++++++++++++++
 lib/test_sysctl.c    |  9 +++---
 2 files changed, 76 insertions(+), 5 deletions(-)

diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c
index dd658ca18793..f9efc146f219 100644
--- a/kernel/sysctl-test.c
+++ b/kernel/sysctl-test.c
@@ -699,10 +699,56 @@ static int sysctl_test_entry_write(struct kunit *test, const struct ctl_table *t
  * entry must not change what is stored, so a stray context pointer cannot
  * redirect a write to the wrong address.
  */
+static void sysctl_test_priv_standard_handler(struct kunit *test)
+{
+	int value = 0;
+	struct sysctl_test_demo_ctx {
+		int marker;
+	} ctx = { .marker = -1 };
+	const struct ctl_table table = {
+		.procname = "standard",
+		.data = &value,
+		.maxlen = sizeof(value),
+		.mode = 0644,
+		.proc_handler = proc_dointvec_minmax,
+		.min = 0,
+		.max = 100,
+		.priv = &ctx,
+	};
+
+	sysctl_test_entry_roundtrip(test, &table, "42");
+	KUNIT_EXPECT_EQ(test, value, 42);
+	KUNIT_EXPECT_EQ(test, ctx.marker, -1);
+	KUNIT_EXPECT_PTR_EQ(test, table.data, &value);
+	KUNIT_EXPECT_PTR_EQ(test, table.priv, &ctx);
+}
+
 /*
  * Registration refuses priv on a standard handler: the context would be
  * silently ignored, which is always a configuration mistake.
  */
+static void sysctl_test_priv_standard_handler_reject(struct kunit *test)
+{
+	int value = 0;
+	struct sysctl_test_demo_ctx {
+		int marker;
+	} ctx = { .marker = -1 };
+	const struct ctl_table table = {
+		.procname = "invalid_priv",
+		.data = &value,
+		.maxlen = sizeof(value),
+		.mode = 0644,
+		.proc_handler = proc_dointvec_minmax,
+		.min = 0,
+		.max = 100,
+		.priv = &ctx,
+	};
+
+	KUNIT_EXPECT_PTR_EQ(test, NULL,
+			    register_sysctl_sz("sysctl_kunit_priv_invalid",
+					       &table, 1));
+}
+
 
 /*
  * A dynamic range wrapper reads its related limit through priv on each call
@@ -1159,6 +1205,29 @@ static void sysctl_test_context_registration_lifecycle(struct kunit *test)
  * at the caller-owned objects; the handler resolves storage through data and
  * context through priv on every call.
  */
+static void sysctl_test_priv_registration_lifecycle(struct kunit *test)
+{
+	struct sysctl_test_lifecycle ctx = {};
+	struct ctl_table table = SYSCTL_TEST_CTX(ctx.value, "priv_lifecycle",
+						 0644,
+						 sysctl_test_lifecycle_handler, ctx);
+	struct ctl_table_header *header;
+
+	header = register_sysctl_sz("sysctl_kunit_priv", &table, 1);
+	KUNIT_ASSERT_NOT_NULL(test, header);
+	kunit_add_action(test, sysctl_test_unregister_table, header);
+
+	sysctl_test_entry_roundtrip(test, &table, "23");
+	KUNIT_EXPECT_EQ(test, ctx.value, 23);
+	KUNIT_EXPECT_PTR_EQ(test, table.priv, &ctx);
+	KUNIT_EXPECT_PTR_EQ(test, table.data, &ctx.value);
+
+	kunit_release_action(test, sysctl_test_unregister_table, header);
+	sysctl_test_entry_roundtrip(test, &table, "17");
+	KUNIT_EXPECT_EQ(test, ctx.value, 17);
+	KUNIT_EXPECT_PTR_EQ(test, table.priv, &ctx);
+}
+
 /* Registration rejects numeric bounds that cannot represent the element type. */
 static void sysctl_test_context_registration_reject(struct kunit *test)
 {
@@ -1381,6 +1450,9 @@ 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_priv_standard_handler),
+	KUNIT_CASE(sysctl_test_priv_standard_handler_reject),
+	KUNIT_CASE(sysctl_test_priv_registration_lifecycle),
 	KUNIT_CASE(sysctl_test_entry_null_handler),
 	KUNIT_CASE(sysctl_test_entry_explicit_handler),
 	KUNIT_CASE(sysctl_test_entry_int_limits),
diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c
index 1411896fdbd0..64c82fd53975 100644
--- a/lib/test_sysctl.c
+++ b/lib/test_sysctl.c
@@ -97,7 +97,7 @@ static struct test_sysctl_data test_data = {
 static int test_sysctl_dynamic_value(const struct ctl_table *table, int dir,
 				     void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct test_sysctl_range_context *context = table->data;
+	struct test_sysctl_range_context *context = table->priv;
 	struct ctl_table resolved = *table;
 
 	resolved.data = &context->value;
@@ -215,12 +215,11 @@ static const struct ctl_table test_table[] = {
 	},
 	{
 		.procname	= "dynamic_value",
-		.data		= &test_data.dynamic,
-		.maxlen		= sizeof(test_data.dynamic),
+		.data		= &test_data.dynamic.value,
+		.maxlen		= sizeof(test_data.dynamic.value),
 		.mode		= 0644,
 		.proc_handler	= test_sysctl_dynamic_value,
-		.min		= 0,
-		.max		= ULONG_MAX,
+		.priv		= &test_data.dynamic,
 	},
 	{
 		.procname	= "int_zero",
-- 
2.25.1


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

* [RFC PATCH 06/39] sysctl: convert core table bounds to numeric values
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (4 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 05/39] sysctl: cover priv in KUnit and the debug test module wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 07/39] sysctl: store architecture sysctl bounds by value wen.yang
                   ` (32 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Use numeric bounds for the write-mode table and static-key handler.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 kernel/sysctl.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 7d0bcd2b24ef..ab25c75cb3ac 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1348,8 +1348,8 @@ int proc_do_static_key(const struct ctl_table *table, int dir,
 		.data   = &val,
 		.maxlen = sizeof(val),
 		.mode   = table->mode,
-		.extra1 = SYSCTL_ZERO,
-		.extra2 = SYSCTL_ONE,
+		.min    = 0,
+		.max    = 1,
 	};
 
 	if (SYSCTL_USER_TO_KERN(dir) && !capable(CAP_SYS_ADMIN))
@@ -1375,8 +1375,8 @@ static const struct ctl_table sysctl_subsys_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_NEG_ONE,
-		.extra2		= SYSCTL_ONE,
+		.min		= -1,
+		.max		= 1,
 	},
 	{
 		.procname	= "ngroups_max",
-- 
2.25.1


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

* [RFC PATCH 07/39] sysctl: store architecture sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (5 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 06/39] sysctl: convert core table bounds to numeric values wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 08/39] sysctl: store crypto " wen.yang
                   ` (31 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Replace architecture bound pointers with numeric limits.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 arch/arm64/kernel/armv8_deprecated.c |  4 ++--
 arch/arm64/kernel/process.c          |  4 ++--
 arch/riscv/kernel/process.c          |  4 ++--
 arch/s390/appldata/appldata_base.c   | 12 +++++++-----
 arch/s390/kernel/hiperdispatch.c     |  4 ++--
 arch/s390/kernel/topology.c          |  8 ++++----
 arch/sh/kernel/vsyscall/vsyscall.c   |  4 ++--
 arch/x86/entry/vdso/vdso32-setup.c   |  4 ++--
 arch/x86/kernel/cpu/bus_lock.c       |  4 ++--
 9 files changed, 25 insertions(+), 23 deletions(-)

diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c
index b7a1f8b788bb..1c143e3a8d09 100644
--- a/arch/arm64/kernel/armv8_deprecated.c
+++ b/arch/arm64/kernel/armv8_deprecated.c
@@ -566,8 +566,8 @@ static void __init register_insn_emulation(struct insn_emulation *insn)
 
 		sysctl->procname = insn->name;
 		sysctl->data = &insn->current_mode;
-		sysctl->extra1 = &insn->min;
-		sysctl->extra2 = &insn->max;
+		sysctl->min = insn->min;
+		sysctl->max = insn->max;
 		sysctl->proc_handler = emulation_proc_handler;
 
 		register_sysctl_sz("abi", sysctl, 1);
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index 581f80e9b9b7..9db8f8bedc06 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -944,8 +944,8 @@ static const struct ctl_table tagged_addr_sysctl_table[] = {
 		.data		= &tagged_addr_disabled,
 		.maxlen		= sizeof(int),
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c
index 34c1d498d1ca..6a1408dbf423 100644
--- a/arch/riscv/kernel/process.c
+++ b/arch/riscv/kernel/process.c
@@ -429,8 +429,8 @@ static const struct ctl_table tagged_addr_sysctl_table[] = {
 		.data		= &tagged_addr_disabled,
 		.maxlen		= sizeof(int),
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c
index f10b2f040cc2..012f9eae8e62 100644
--- a/arch/s390/appldata/appldata_base.c
+++ b/arch/s390/appldata/appldata_base.c
@@ -12,6 +12,7 @@
 #define pr_fmt(fmt) "appldata: " fmt
 
 #include <linux/export.h>
+#include <linux/limits.h>
 #include <linux/module.h>
 #include <linux/sched/stat.h>
 #include <linux/init.h>
@@ -250,9 +251,9 @@ appldata_timer_handler(const struct ctl_table *ctl, int write,
 	struct ctl_table ctl_entry = {
 		.procname	= ctl->procname,
 		.data		= &timer_active,
+		.min		= 0,
 		.maxlen		= sizeof(int),
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.max		= 1,
 	};
 
 	rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
@@ -281,8 +282,9 @@ appldata_interval_handler(const struct ctl_table *ctl, int write,
 	struct ctl_table ctl_entry = {
 		.procname	= ctl->procname,
 		.data		= &interval,
+		.min		= 1,
 		.maxlen		= sizeof(int),
-		.extra1		= SYSCTL_ONE,
+		.max		= INT_MAX,
 	};
 
 	rc = proc_dointvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
@@ -312,9 +314,9 @@ appldata_generic_handler(const struct ctl_table *ctl, int write,
 	int active;
 	struct ctl_table ctl_entry = {
 		.data		= &active,
+		.min		= 0,
 		.maxlen		= sizeof(int),
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.max		= 1,
 	};
 
 	found = 0;
diff --git a/arch/s390/kernel/hiperdispatch.c b/arch/s390/kernel/hiperdispatch.c
index 1397dab8a677..4455b081c652 100644
--- a/arch/s390/kernel/hiperdispatch.c
+++ b/arch/s390/kernel/hiperdispatch.c
@@ -272,9 +272,9 @@ static int hiperdispatch_ctl_handler(const struct ctl_table *ctl, int write,
 	struct ctl_table ctl_entry = {
 		.procname	= ctl->procname,
 		.data		= &hiperdispatch,
+		.min		= 0,
 		.maxlen		= sizeof(int),
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.max		= 1,
 	};
 
 	hiperdispatch = hd_enabled;
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c
index 42fc0294f543..8ca541e2f50a 100644
--- a/arch/s390/kernel/topology.c
+++ b/arch/s390/kernel/topology.c
@@ -624,9 +624,9 @@ static int topology_ctl_handler(const struct ctl_table *ctl, int write,
 	struct ctl_table ctl_entry = {
 		.procname	= ctl->procname,
 		.data		= &enabled,
+		.min		= 0,
 		.maxlen		= sizeof(int),
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.max		= 1,
 	};
 
 	rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
@@ -653,9 +653,9 @@ static int polarization_ctl_handler(const struct ctl_table *ctl, int write,
 	struct ctl_table ctl_entry = {
 		.procname	= ctl->procname,
 		.data		= &polarization,
+		.min		= 0,
 		.maxlen		= sizeof(int),
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.max		= 1,
 	};
 
 	polarization = cpu_management;
diff --git a/arch/sh/kernel/vsyscall/vsyscall.c b/arch/sh/kernel/vsyscall/vsyscall.c
index 1563dcc55fd3..fb4f465f380b 100644
--- a/arch/sh/kernel/vsyscall/vsyscall.c
+++ b/arch/sh/kernel/vsyscall/vsyscall.c
@@ -38,8 +38,8 @@ static const struct ctl_table vdso_table[] = {
 		.maxlen		= sizeof(vdso_enabled),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/arch/x86/entry/vdso/vdso32-setup.c b/arch/x86/entry/vdso/vdso32-setup.c
index 8894013eea1d..53e2e2cd267c 100644
--- a/arch/x86/entry/vdso/vdso32-setup.c
+++ b/arch/x86/entry/vdso/vdso32-setup.c
@@ -66,8 +66,8 @@ static const struct ctl_table vdso_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/arch/x86/kernel/cpu/bus_lock.c b/arch/x86/kernel/cpu/bus_lock.c
index 177f964d4d36..a9a8177ffccf 100644
--- a/arch/x86/kernel/cpu/bus_lock.c
+++ b/arch/x86/kernel/cpu/bus_lock.c
@@ -58,8 +58,8 @@ static const struct ctl_table sld_sysctls[] = {
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
-- 
2.25.1


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

* [RFC PATCH 08/39] sysctl: store crypto sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (6 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 07/39] sysctl: store architecture sysctl bounds by value wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 09/39] sysctl: store device " wen.yang
                   ` (30 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Replace crypto bound pointers with numeric limits.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 crypto/af_alg.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/crypto/af_alg.c b/crypto/af_alg.c
index ab84c4488a15..8c7559c3fdca 100644
--- a/crypto/af_alg.c
+++ b/crypto/af_alg.c
@@ -38,8 +38,8 @@ static const struct ctl_table af_alg_table[] = {
 		.maxlen         = sizeof(int),
 		.mode           = 0644,
 		.proc_handler   = proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 };
 
-- 
2.25.1


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

* [RFC PATCH 09/39] sysctl: store device sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (7 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 08/39] sysctl: store crypto " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 10/39] sysctl: store filesystem " wen.yang
                   ` (29 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Replace device bound pointers with numeric limits.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 drivers/base/firmware_loader/fallback_table.c | 8 ++++----
 drivers/gpu/drm/xe/xe_observation.c           | 4 ++--
 drivers/hv/hv_common.c                        | 4 ++--
 drivers/infiniband/core/iwcm.c                | 5 +++--
 drivers/infiniband/core/ucma.c                | 5 +++--
 drivers/perf/arm_pmuv3.c                      | 4 ++--
 drivers/perf/riscv_pmu_sbi.c                  | 4 ++--
 drivers/scsi/scsi_sysctl.c                    | 5 +++--
 drivers/tty/tty_io.c                          | 4 ++--
 drivers/xen/balloon.c                         | 4 ++--
 10 files changed, 25 insertions(+), 22 deletions(-)

diff --git a/drivers/base/firmware_loader/fallback_table.c b/drivers/base/firmware_loader/fallback_table.c
index c8afc501a8a4..72a47fcf5f45 100644
--- a/drivers/base/firmware_loader/fallback_table.c
+++ b/drivers/base/firmware_loader/fallback_table.c
@@ -32,8 +32,8 @@ static const struct ctl_table firmware_config_table[] = {
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
 		.proc_handler   = proc_douintvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "ignore_sysfs_fallback",
@@ -41,8 +41,8 @@ static const struct ctl_table firmware_config_table[] = {
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
 		.proc_handler   = proc_douintvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/drivers/gpu/drm/xe/xe_observation.c b/drivers/gpu/drm/xe/xe_observation.c
index 39e05b9131a7..cd56d29d77fe 100644
--- a/drivers/gpu/drm/xe/xe_observation.c
+++ b/drivers/gpu/drm/xe/xe_observation.c
@@ -97,8 +97,8 @@ static const struct ctl_table observation_ctl_table[] = {
 	 .maxlen = sizeof(xe_observation_paranoid),
 	 .mode = 0644,
 	 .proc_handler = proc_dointvec_minmax,
-	 .extra1 = SYSCTL_ZERO,
-	 .extra2 = SYSCTL_ONE,
+	 .min          = 0,
+	 .max          = 1,
 	 },
 };
 
diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
index 27b9b79d4f4c..1018c8a8e6bb 100644
--- a/drivers/hv/hv_common.c
+++ b/drivers/hv/hv_common.c
@@ -147,8 +147,8 @@ static const struct ctl_table hv_ctl_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
+		.min		= 0,
+		.max		= 1
 	},
 };
 
diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c
index 4b05867dad89..0925996aa0fc 100644
--- a/drivers/infiniband/core/iwcm.c
+++ b/drivers/infiniband/core/iwcm.c
@@ -36,6 +36,7 @@
  *
  */
 #include <linux/dma-mapping.h>
+#include <linux/limits.h>
 #include <linux/err.h>
 #include <linux/idr.h>
 #include <linux/interrupt.h>
@@ -109,8 +110,8 @@ static struct ctl_table iwcm_ctl_table[] = {
 		.maxlen		= sizeof(default_backlog),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 };
 
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index a15182f7a7c5..4c9f15846d18 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -31,6 +31,7 @@
  */
 
 #include <linux/completion.h>
+#include <linux/limits.h>
 #include <linux/file.h>
 #include <linux/mutex.h>
 #include <linux/poll.h>
@@ -70,8 +71,8 @@ static struct ctl_table ucma_ctl_table[] = {
 		.maxlen		= sizeof max_backlog,
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 };
 
diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c
index 03359e078301..ec934ddf2f92 100644
--- a/drivers/perf/arm_pmuv3.c
+++ b/drivers/perf/arm_pmuv3.c
@@ -1462,8 +1462,8 @@ static const struct ctl_table armv8_pmu_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode           = 0644,
 		.proc_handler	= armv8pmu_proc_user_access_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 21a2078eab98..0e7d733a0fcf 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -1462,8 +1462,8 @@ static const struct ctl_table sbi_pmu_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode           = 0644,
 		.proc_handler	= riscv_pmu_proc_user_access_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 };
 
diff --git a/drivers/scsi/scsi_sysctl.c b/drivers/scsi/scsi_sysctl.c
index 055a03a83ad6..85aeffda94cd 100644
--- a/drivers/scsi/scsi_sysctl.c
+++ b/drivers/scsi/scsi_sysctl.c
@@ -4,6 +4,7 @@
  */
 
 #include <linux/errno.h>
+#include <linux/limits.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/sysctl.h>
@@ -18,8 +19,8 @@ static const struct ctl_table scsi_table[] = {
 	  .maxlen	= sizeof(scsi_logging_level),
 	  .mode		= 0644,
 	  .proc_handler	= proc_dointvec_minmax,
-	  .extra1	= SYSCTL_ZERO,
-	  .extra2	= SYSCTL_INT_MAX },
+	  .min	= 0,
+	  .max	= INT_MAX },
 };
 
 static struct ctl_table_header *scsi_table_header;
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 1a6c8a1bc764..2898c7bc774f 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -3632,8 +3632,8 @@ static const struct ctl_table tty_table[] = {
 		.maxlen		= sizeof(tty_ldisc_autoload),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index 373042fe501b..8883cfa90a8f 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -92,8 +92,8 @@ static const struct ctl_table balloon_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
-- 
2.25.1


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

* [RFC PATCH 10/39] sysctl: store filesystem sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (8 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 09/39] sysctl: store device " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 11/39] sysctl: store message-queue " wen.yang
                   ` (28 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Replace filesystem bound pointers with numeric limits.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 fs/coredump.c                      | 15 ++++----
 fs/dcache.c                        | 13 +++++--
 fs/devpts/inode.c                  | 10 ++---
 fs/drop_caches.c                   |  4 +-
 fs/eventpoll.c                     |  7 +---
 fs/exec.c                          | 10 ++---
 fs/file_table.c                    | 12 ++++--
 fs/fs-writeback.c                  |  4 +-
 fs/fuse/sysctl.c                   | 24 +++++-------
 fs/lockd/svc.c                     | 18 ++++-----
 fs/namei.c                         | 16 ++++----
 fs/namespace.c                     |  3 +-
 fs/nfs/nfs4sysctl.c                |  6 +--
 fs/notify/fanotify/fanotify_user.c | 17 ++++-----
 fs/notify/inotify/inotify_user.c   | 14 +++----
 fs/pipe.c                          |  6 +--
 fs/sysctls.c                       |  8 ++--
 fs/verity/init.c                   |  4 +-
 fs/xfs/xfs_sysctl.c                | 59 +++++++++++++++---------------
 19 files changed, 121 insertions(+), 129 deletions(-)

diff --git a/fs/coredump.c b/fs/coredump.c
index b25ef2ffbdee..295f9bd0353b 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/slab.h>
+#include <linux/limits.h>
 #include <linux/file.h>
 #include <linux/fdtable.h>
 #include <linux/freezer.h>
@@ -1722,8 +1723,6 @@ static int proc_dostring_coredump(const struct ctl_table *table, int write,
 	return 0;
 }
 
-static const unsigned int core_file_note_size_min = CORE_FILE_NOTE_SIZE_DEFAULT;
-static const unsigned int core_file_note_size_max = CORE_FILE_NOTE_SIZE_MAX;
 static char core_modes[] = {
 	"file\npipe"
 #ifdef CONFIG_UNIX
@@ -1752,8 +1751,8 @@ static const struct ctl_table coredump_sysctls[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname       = "core_file_note_size_limit",
@@ -1761,8 +1760,8 @@ static const struct ctl_table coredump_sysctls[] = {
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= (unsigned int *)&core_file_note_size_min,
-		.extra2		= (unsigned int *)&core_file_note_size_max,
+		.min		= CORE_FILE_NOTE_SIZE_DEFAULT,
+		.max		= CORE_FILE_NOTE_SIZE_MAX,
 	},
 	{
 		.procname	= "core_sort_vma",
@@ -1770,8 +1769,8 @@ static const struct ctl_table coredump_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "core_modes",
diff --git a/fs/dcache.c b/fs/dcache.c
index 37d021d22a7d..0e8ebb0a9b93 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -16,6 +16,7 @@
  */
 
 #include <linux/ratelimit.h>
+#include <linux/limits.h>
 #include <linux/string.h>
 #include <linux/mm.h>
 #include <linux/fs.h>
@@ -207,6 +208,8 @@ static const struct ctl_table fs_dcache_sysctls[] = {
 		.maxlen		= 6*sizeof(long),
 		.mode		= 0444,
 		.proc_handler	= proc_nr_dentry,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "dentry-negative",
@@ -214,8 +217,8 @@ static const struct ctl_table fs_dcache_sysctls[] = {
 		.maxlen		= sizeof(dentry_negative_policy),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
@@ -226,7 +229,8 @@ static const struct ctl_table vm_dcache_sysctls[] = {
 		.maxlen		= sizeof(sysctl_vfs_cache_pressure),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "vfs_cache_pressure_denom",
@@ -234,7 +238,8 @@ static const struct ctl_table vm_dcache_sysctls[] = {
 		.maxlen		= sizeof(sysctl_vfs_cache_pressure_denom),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE_HUNDRED,
+		.min		= 100,
+		.max		= INT_MAX,
 	},
 };
 
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index bd1e8eb26edb..7aa5bccd1074 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -42,8 +42,6 @@
  */
 static int pty_limit = NR_UNIX98_PTY_DEFAULT;
 static int pty_reserve = NR_UNIX98_PTY_RESERVE;
-static int pty_limit_min;
-static int pty_limit_max = INT_MAX;
 static atomic_t pty_count = ATOMIC_INIT(0);
 
 static const struct ctl_table pty_table[] = {
@@ -53,16 +51,16 @@ static const struct ctl_table pty_table[] = {
 		.mode		= 0644,
 		.data		= &pty_limit,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &pty_limit_min,
-		.extra2		= &pty_limit_max,
+		.min		= 0,
+		.max		= INT_MAX,
 	}, {
 		.procname	= "reserve",
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.data		= &pty_reserve,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &pty_limit_min,
-		.extra2		= &pty_limit_max,
+		.min		= 0,
+		.max		= INT_MAX,
 	}, {
 		.procname	= "nr",
 		.maxlen		= sizeof(int),
diff --git a/fs/drop_caches.c b/fs/drop_caches.c
index 49f56a598ecb..835c41fe443c 100644
--- a/fs/drop_caches.c
+++ b/fs/drop_caches.c
@@ -85,8 +85,8 @@ static const struct ctl_table drop_caches_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0200,
 		.proc_handler	= drop_caches_sysctl_handler,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_FOUR,
+		.min		= 1,
+		.max		= 4,
 	},
 };
 
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index f48b829a710f..957ab9ff2398 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -520,9 +520,6 @@ static void unlist_file(struct epitems_head *head)
 
 #include <linux/sysctl.h>
 
-static long long_zero;
-static long long_max = LONG_MAX;
-
 static const struct ctl_table epoll_table[] = {
 	{
 		.procname	= "max_user_watches",
@@ -530,8 +527,8 @@ static const struct ctl_table epoll_table[] = {
 		.maxlen		= sizeof(max_user_watches),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra1		= &long_zero,
-		.extra2		= &long_max,
+		.min		= 0,
+		.max		= LONG_MAX,
 	},
 };
 
diff --git a/fs/exec.c b/fs/exec.c
index 8d2b14e1d455..7ac813f138b3 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -2078,8 +2078,8 @@ COMPAT_SYSCALL_DEFINE5(execveat, int, fd,
 
 #ifdef CONFIG_SYSCTL
 
-static int proc_dointvec_minmax_coredump(const struct ctl_table *table, int write,
-		void *buffer, size_t *lenp, loff_t *ppos)
+static int proc_dointvec_range_coredump(const struct ctl_table *table, int write,
+					void *buffer, size_t *lenp, loff_t *ppos)
 {
 	int error, old = READ_ONCE(suid_dumpable);
 
@@ -2096,9 +2096,9 @@ static const struct ctl_table fs_exec_sysctls[] = {
 		.data		= &suid_dumpable,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax_coredump,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.proc_handler	= proc_dointvec_range_coredump,
+		.min		= 0,
+		.max		= 2,
 	},
 };
 
diff --git a/fs/file_table.c b/fs/file_table.c
index 9c2a5e06d678..cab577116511 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/string.h>
+#include <linux/limits.h>
 #include <linux/slab.h>
 #include <linux/file.h>
 #include <linux/init.h>
@@ -142,6 +143,8 @@ static const struct ctl_table fs_stat_sysctls[] = {
 		.maxlen		= sizeof(files_stat),
 		.mode		= 0444,
 		.proc_handler	= proc_nr_files,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "file-max",
@@ -149,8 +152,8 @@ static const struct ctl_table fs_stat_sysctls[] = {
 		.maxlen		= sizeof(files_stat.max_files),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra1		= SYSCTL_LONG_ZERO,
-		.extra2		= SYSCTL_LONG_MAX,
+		.min		= 0,
+		.max		= LONG_MAX,
 	},
 	{
 		.procname	= "nr_open",
@@ -158,8 +161,9 @@ static const struct ctl_table fs_stat_sysctls[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= &sysctl_nr_open_min,
-		.extra2		= &sysctl_nr_open_max,
+		.min		= BITS_PER_LONG,
+		.max		= (SIZE_MAX / sizeof(void *) < INT_MAX ?
+				   SIZE_MAX / sizeof(void *) : INT_MAX) & -BITS_PER_LONG,
 	},
 };
 
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 58a6403780aa..559041eb7759 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -15,6 +15,7 @@
  */
 
 #include <linux/sched/sysctl.h>
+#include <linux/limits.h>
 #include <linux/kernel.h>
 #include <linux/export.h>
 #include <linux/spinlock.h>
@@ -2662,7 +2663,8 @@ static const struct ctl_table vm_fs_writeback_table[] = {
 		.maxlen		= sizeof(dirtytime_expire_interval),
 		.mode		= 0644,
 		.proc_handler	= dirtytime_interval_handler,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 };
 
diff --git a/fs/fuse/sysctl.c b/fs/fuse/sysctl.c
index 74eca5ce9a2c..0aca807d190e 100644
--- a/fs/fuse/sysctl.c
+++ b/fs/fuse/sysctl.c
@@ -11,15 +11,6 @@
 
 static struct ctl_table_header *fuse_table_header;
 
-/* Bound by fuse_init_out max_pages, which is a u16 */
-static unsigned int sysctl_fuse_max_pages_limit = 65535;
-
-/*
- * fuse_init_out request timeouts are u16.
- * This goes up to ~18 hours, which is plenty for a timeout.
- */
-static unsigned int sysctl_fuse_req_timeout_limit = 65535;
-
 static const struct ctl_table fuse_sysctl_table[] = {
 	{
 		.procname	= "max_pages_limit",
@@ -27,8 +18,9 @@ static const struct ctl_table fuse_sysctl_table[] = {
 		.maxlen		= sizeof(fuse_max_pages_limit),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &sysctl_fuse_max_pages_limit,
+		.min		= 1,
+		/* Bound by fuse_init_out max_pages, which is a u16. */
+		.max		= 65535,
 	},
 	{
 		.procname	= "default_request_timeout",
@@ -36,8 +28,9 @@ static const struct ctl_table fuse_sysctl_table[] = {
 		.maxlen		= sizeof(fuse_default_req_timeout),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &sysctl_fuse_req_timeout_limit,
+		.min		= 0,
+		/* u16 request timeout: up to ~18 hours. */
+		.max		= 65535,
 	},
 	{
 		.procname	= "max_request_timeout",
@@ -45,8 +38,9 @@ static const struct ctl_table fuse_sysctl_table[] = {
 		.maxlen		= sizeof(fuse_max_req_timeout),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &sysctl_fuse_req_timeout_limit,
+		.min		= 0,
+		/* u16 request timeout: up to ~18 hours. */
+		.max		= 65535,
 	},
 };
 
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index f0e1a58c9106..f45ab868d05f 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -78,7 +78,6 @@ static const unsigned long	nlm_timeout_min = 3;
 static const unsigned long	nlm_timeout_max = 20;
 
 #ifdef CONFIG_SYSCTL
-static const int		nlm_port_min = 0, nlm_port_max = 65535;
 static struct ctl_table_header * nlm_sysctl_table;
 #endif
 
@@ -432,8 +431,8 @@ static const struct ctl_table nlm_sysctls[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra1		= (unsigned long *) &nlm_grace_period_min,
-		.extra2		= (unsigned long *) &nlm_grace_period_max,
+		.min		= 0,
+		.max		= 240,
 	},
 	{
 		.procname	= "nlm_timeout",
@@ -441,8 +440,8 @@ static const struct ctl_table nlm_sysctls[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra1		= (unsigned long *) &nlm_timeout_min,
-		.extra2		= (unsigned long *) &nlm_timeout_max,
+		.min		= 3,
+		.max		= 20,
 	},
 	{
 		.procname	= "nlm_udpport",
@@ -450,8 +449,8 @@ static const struct ctl_table nlm_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= (int *) &nlm_port_min,
-		.extra2		= (int *) &nlm_port_max,
+		.min		= 0,
+		.max		= 65535,
 	},
 	{
 		.procname	= "nlm_tcpport",
@@ -459,8 +458,8 @@ static const struct ctl_table nlm_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= (int *) &nlm_port_min,
-		.extra2		= (int *) &nlm_port_max,
+		.min		= 0,
+		.max		= 65535,
 	},
 	{
 		.procname	= "nsm_use_hostnames",
@@ -475,7 +474,6 @@ static const struct ctl_table nlm_sysctls[] = {
 		.maxlen		= sizeof(nsm_local_state),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec,
-		.extra1		= SYSCTL_ZERO,
 	},
 };
 
diff --git a/fs/namei.c b/fs/namei.c
index 96c65940391a..5c13f63ec11f 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1214,8 +1214,8 @@ static const struct ctl_table namei_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "protected_hardlinks",
@@ -1223,8 +1223,8 @@ static const struct ctl_table namei_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "protected_fifos",
@@ -1232,8 +1232,8 @@ static const struct ctl_table namei_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname	= "protected_regular",
@@ -1241,8 +1241,8 @@ static const struct ctl_table namei_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 };
 
diff --git a/fs/namespace.c b/fs/namespace.c
index 9f6ec35c7b9c..c88483ff0f55 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -6774,7 +6774,8 @@ static const struct ctl_table fs_namespace_sysctls[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 };
 
diff --git a/fs/nfs/nfs4sysctl.c b/fs/nfs/nfs4sysctl.c
index d1a92d8f8ba4..189f601c5c65 100644
--- a/fs/nfs/nfs4sysctl.c
+++ b/fs/nfs/nfs4sysctl.c
@@ -13,8 +13,6 @@
 #include "nfs4idmap.h"
 #include "callback.h"
 
-static const int nfs_set_port_min;
-static const int nfs_set_port_max = 65535;
 static struct ctl_table_header *nfs4_callback_sysctl_table;
 
 static const struct ctl_table nfs4_cb_sysctls[] = {
@@ -24,8 +22,8 @@ static const struct ctl_table nfs4_cb_sysctls[] = {
 		.maxlen = sizeof(int),
 		.mode = 0644,
 		.proc_handler = proc_dointvec_minmax,
-		.extra1 = (int *)&nfs_set_port_min,
-		.extra2 = (int *)&nfs_set_port_max,
+		.min          = 0,
+		.max          = 65535,
 	},
 	{
 		.procname = "idmap_cache_timeout",
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 63c9759fc3b0..1caea0461542 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -57,9 +57,6 @@ static int perm_group_timeout __read_mostly;
 
 #include <linux/sysctl.h>
 
-static long ft_zero = 0;
-static long ft_int_max = INT_MAX;
-
 static const struct ctl_table fanotify_table[] = {
 	{
 		.procname	= "max_user_groups",
@@ -67,8 +64,8 @@ static const struct ctl_table fanotify_table[] = {
 		.maxlen		= sizeof(long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra1		= &ft_zero,
-		.extra2		= &ft_int_max,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "max_user_marks",
@@ -76,8 +73,8 @@ static const struct ctl_table fanotify_table[] = {
 		.maxlen		= sizeof(long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra1		= &ft_zero,
-		.extra2		= &ft_int_max,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "max_queued_events",
@@ -85,7 +82,8 @@ static const struct ctl_table fanotify_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "watchdog_timeout",
@@ -93,7 +91,8 @@ static const struct ctl_table fanotify_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 };
 
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index 5f19c24ec187..43f1a9481a73 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -55,9 +55,6 @@ struct kmem_cache *inotify_inode_mark_cachep __ro_after_init;
 
 #include <linux/sysctl.h>
 
-static long it_zero = 0;
-static long it_int_max = INT_MAX;
-
 static const struct ctl_table inotify_table[] = {
 	{
 		.procname	= "max_user_instances",
@@ -65,8 +62,8 @@ static const struct ctl_table inotify_table[] = {
 		.maxlen		= sizeof(long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra1		= &it_zero,
-		.extra2		= &it_int_max,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "max_user_watches",
@@ -74,8 +71,8 @@ static const struct ctl_table inotify_table[] = {
 		.maxlen		= sizeof(long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra1		= &it_zero,
-		.extra2		= &it_int_max,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "max_queued_events",
@@ -83,7 +80,8 @@ static const struct ctl_table inotify_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 };
 
diff --git a/fs/pipe.c b/fs/pipe.c
index c41ab753c893..41710777c94b 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1579,8 +1579,7 @@ static int do_proc_uint_conv_pipe_maxsz(bool *negp, ulong *u_ptr, uint *k_ptr,
 					int dir, const struct ctl_table *table)
 {
 	return proc_uint_conv(u_ptr, k_ptr, dir, table, true,
-			      u2k_pipe_maxsz,
-			      proc_uint_k2u_conv);
+				    u2k_pipe_maxsz, proc_uint_k2u_conv);
 }
 
 static int proc_dopipe_max_size(const struct ctl_table *table, int write,
@@ -1597,7 +1596,8 @@ static const struct ctl_table fs_pipe_sysctls[] = {
 		.maxlen		= sizeof(pipe_max_size),
 		.mode		= 0644,
 		.proc_handler	= proc_dopipe_max_size,
-		.extra1		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= UINT_MAX,
 	},
 	{
 		.procname	= "pipe-user-pages-hard",
diff --git a/fs/sysctls.c b/fs/sysctls.c
index ad429dffeb4b..64e5aa0da9ff 100644
--- a/fs/sysctls.c
+++ b/fs/sysctls.c
@@ -14,8 +14,8 @@ static const struct ctl_table fs_shared_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_MAXOLDUID,
+		.min		= 0,
+		.max		= 65535,
 	},
 	{
 		.procname	= "overflowgid",
@@ -23,8 +23,8 @@ static const struct ctl_table fs_shared_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_MAXOLDUID,
+		.min		= 0,
+		.max		= 65535,
 	},
 };
 
diff --git a/fs/verity/init.c b/fs/verity/init.c
index 3aa55dec88fc..be78ec9fe0a0 100644
--- a/fs/verity/init.c
+++ b/fs/verity/init.c
@@ -19,8 +19,8 @@ static const struct ctl_table fsverity_sysctl_table[] = {
 		.maxlen         = sizeof(int),
 		.mode           = 0644,
 		.proc_handler   = proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min            = 0,
+		.max            = 1,
 	},
 #endif
 };
diff --git a/fs/xfs/xfs_sysctl.c b/fs/xfs/xfs_sysctl.c
index 7f32d282dc88..693765c284cc 100644
--- a/fs/xfs/xfs_sysctl.c
+++ b/fs/xfs/xfs_sysctl.c
@@ -51,12 +51,11 @@ xfs_panic_mask_proc_handler(
 #endif /* CONFIG_PROC_FS */
 
 static inline int
-xfs_deprecated_dointvec_minmax(
-	const struct ctl_table	*ctl,
-	int			write,
-	void			*buffer,
-	size_t			*lenp,
-	loff_t			*ppos)
+xfs_deprecated_dointvec_range(const struct ctl_table *ctl,
+			      int			write,
+			      void			*buffer,
+			      size_t			*lenp,
+			      loff_t			*ppos)
 {
 	if (write) {
 		printk_ratelimited(KERN_WARNING
@@ -73,8 +72,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= xfs_panic_mask_proc_handler,
-		.extra1		= &xfs_params.panic_mask.min,
-		.extra2		= &xfs_params.panic_mask.max
+		.min		= 0,
+		.max		= XFS_PTAG_MASK
 	},
 
 	{
@@ -83,8 +82,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xfs_params.error_level.min,
-		.extra2		= &xfs_params.error_level.max
+		.min		= 0,
+		.max		= 11
 	},
 	{
 		.procname	= "xfssyncd_centisecs",
@@ -92,8 +91,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xfs_params.syncd_timer.min,
-		.extra2		= &xfs_params.syncd_timer.max
+		.min		= 100,
+		.max		= 7200 * 100
 	},
 	{
 		.procname	= "inherit_sync",
@@ -101,8 +100,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xfs_params.inherit_sync.min,
-		.extra2		= &xfs_params.inherit_sync.max
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "inherit_nodump",
@@ -110,8 +109,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xfs_params.inherit_nodump.min,
-		.extra2		= &xfs_params.inherit_nodump.max
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "inherit_noatime",
@@ -119,8 +118,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xfs_params.inherit_noatim.min,
-		.extra2		= &xfs_params.inherit_noatim.max
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "inherit_nosymlinks",
@@ -128,8 +127,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xfs_params.inherit_nosym.min,
-		.extra2		= &xfs_params.inherit_nosym.max
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "rotorstep",
@@ -137,8 +136,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xfs_params.rotorstep.min,
-		.extra2		= &xfs_params.rotorstep.max
+		.min		= 1,
+		.max		= 255
 	},
 	{
 		.procname	= "inherit_nodefrag",
@@ -146,8 +145,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xfs_params.inherit_nodfrg.min,
-		.extra2		= &xfs_params.inherit_nodfrg.max
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "filestream_centisecs",
@@ -155,8 +154,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xfs_params.fstrm_timer.min,
-		.extra2		= &xfs_params.fstrm_timer.max,
+		.min		= 1,
+		.max		= 3600 * 100,
 	},
 	{
 		.procname	= "speculative_prealloc_lifetime",
@@ -164,8 +163,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xfs_params.blockgc_timer.min,
-		.extra2		= &xfs_params.blockgc_timer.max,
+		.min		= 1,
+		.max		= 3600 * 24,
 	},
 	/* please keep this the last entry */
 #ifdef CONFIG_PROC_FS
@@ -175,8 +174,8 @@ static const struct ctl_table xfs_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= xfs_stats_clear_proc_handler,
-		.extra1		= &xfs_params.stats_clear.min,
-		.extra2		= &xfs_params.stats_clear.max
+		.min		= 0,
+		.max		= 1
 	},
 #endif /* CONFIG_PROC_FS */
 };
-- 
2.25.1


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

* [RFC PATCH 11/39] sysctl: store message-queue sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (9 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 10/39] sysctl: store filesystem " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 12/39] sysctl: store kernel " wen.yang
                   ` (27 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Replace message-queue bound pointers with numeric limits.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 ipc/mq_sysctl.c | 22 ++++++++--------------
 1 file changed, 8 insertions(+), 14 deletions(-)

diff --git a/ipc/mq_sysctl.c b/ipc/mq_sysctl.c
index 0dd12e1c9f53..cf0efc168ce6 100644
--- a/ipc/mq_sysctl.c
+++ b/ipc/mq_sysctl.c
@@ -14,12 +14,6 @@
 #include <linux/slab.h>
 #include <linux/cred.h>
 
-static int msg_max_limit_min = MIN_MSGMAX;
-static int msg_max_limit_max = HARD_MSGMAX;
-
-static int msg_maxsize_limit_min = MIN_MSGSIZEMAX;
-static int msg_maxsize_limit_max = HARD_MSGSIZEMAX;
-
 static const struct ctl_table mq_sysctls[] = {
 	{
 		.procname	= "queues_max",
@@ -34,8 +28,8 @@ static const struct ctl_table mq_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &msg_max_limit_min,
-		.extra2		= &msg_max_limit_max,
+		.min		= MIN_MSGMAX,
+		.max		= HARD_MSGMAX,
 	},
 	{
 		.procname	= "msgsize_max",
@@ -43,8 +37,8 @@ static const struct ctl_table mq_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &msg_maxsize_limit_min,
-		.extra2		= &msg_maxsize_limit_max,
+		.min		= MIN_MSGSIZEMAX,
+		.max		= HARD_MSGSIZEMAX,
 	},
 	{
 		.procname	= "msg_default",
@@ -52,8 +46,8 @@ static const struct ctl_table mq_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &msg_max_limit_min,
-		.extra2		= &msg_max_limit_max,
+		.min		= MIN_MSGMAX,
+		.max		= HARD_MSGMAX,
 	},
 	{
 		.procname	= "msgsize_default",
@@ -61,8 +55,8 @@ static const struct ctl_table mq_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &msg_maxsize_limit_min,
-		.extra2		= &msg_maxsize_limit_max,
+		.min		= MIN_MSGSIZEMAX,
+		.max		= HARD_MSGSIZEMAX,
 	},
 };
 
-- 
2.25.1


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

* [RFC PATCH 12/39] sysctl: store kernel sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (10 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 11/39] sysctl: store message-queue " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 13/39] sysctl: store memory-management " wen.yang
                   ` (26 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Replace kernel bound pointers with numeric limits.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 kernel/bpf/syscall.c      |  8 ++++----
 kernel/delayacct.c        |  4 ++--
 kernel/events/callchain.c |  9 ++++-----
 kernel/events/core.c      |  7 ++++---
 kernel/hung_task.c        | 30 +++++++++++++++++-------------
 kernel/kexec_core.c       |  4 ++--
 kernel/kprobes.c          |  4 ++--
 kernel/kstack_erase.c     |  4 ++--
 kernel/module/main.c      |  4 ++--
 kernel/panic.c            | 13 +++++++++----
 kernel/printk/sysctl.c    | 22 ++++++++++------------
 kernel/rcu/tree_stall.h   |  9 +++++----
 kernel/sched/autogroup.c  |  4 ++--
 kernel/sched/core.c       |  8 ++++----
 kernel/sched/fair.c       |  6 ++++--
 kernel/sched/topology.c   |  4 ++--
 kernel/sys.c              |  8 ++++----
 kernel/time/timer.c       |  4 ++--
 kernel/ucount.c           |  7 ++-----
 kernel/watchdog.c         | 35 +++++++++++++++++------------------
 20 files changed, 100 insertions(+), 94 deletions(-)

diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 90bc7230afa8..8b079e220f00 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -6764,8 +6764,8 @@ static int bpf_stats_handler(const struct ctl_table *table, int write,
 		.data   = &val,
 		.maxlen = sizeof(val),
 		.mode   = table->mode,
-		.extra1 = SYSCTL_ZERO,
-		.extra2 = SYSCTL_ONE,
+		.min    = 0,
+		.max    = 1,
 	};
 
 	if (write && !capable(CAP_SYS_ADMIN))
@@ -6820,8 +6820,8 @@ static const struct ctl_table bpf_syscall_table[] = {
 		.maxlen		= sizeof(sysctl_unprivileged_bpf_disabled),
 		.mode		= 0644,
 		.proc_handler	= bpf_unpriv_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname	= "bpf_stats_enabled",
diff --git a/kernel/delayacct.c b/kernel/delayacct.c
index 479e860aff70..e942f118b5a8 100644
--- a/kernel/delayacct.c
+++ b/kernel/delayacct.c
@@ -82,8 +82,8 @@ static const struct ctl_table kern_delayacct_table[] = {
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
 		.proc_handler   = sysctl_delayacct,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min            = 0,
+		.max            = 1,
 	},
 };
 
diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c
index 9d24b6e0c91f..0990eb237834 100644
--- a/kernel/events/callchain.c
+++ b/kernel/events/callchain.c
@@ -22,7 +22,6 @@ struct callchain_cpus_entries {
 
 int sysctl_perf_event_max_stack __read_mostly = PERF_MAX_STACK_DEPTH;
 int sysctl_perf_event_max_contexts_per_stack __read_mostly = PERF_MAX_CONTEXTS_PER_STACK;
-static const int six_hundred_forty_kb = 640 * 1024;
 
 static inline size_t perf_callchain_entry__sizeof(void)
 {
@@ -307,8 +306,8 @@ static const struct ctl_table callchain_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_perf_event_max_stack),
 		.mode		= 0644,
 		.proc_handler	= perf_event_max_stack_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= (void *)&six_hundred_forty_kb,
+		.min		= 0,
+		.max		= 640 * 1024,
 	},
 	{
 		.procname	= "perf_event_max_contexts_per_stack",
@@ -316,8 +315,8 @@ static const struct ctl_table callchain_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_perf_event_max_contexts_per_stack),
 		.mode		= 0644,
 		.proc_handler	= perf_event_max_stack_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE_THOUSAND,
+		.min		= 0,
+		.max		= 1000,
 	},
 };
 
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 457d75ea7142..339a200f8b00 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -595,7 +595,8 @@ static const struct ctl_table events_core_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_perf_event_sample_rate),
 		.mode		= 0644,
 		.proc_handler	= perf_event_max_sample_rate_handler,
-		.extra1		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "perf_cpu_time_max_percent",
@@ -603,8 +604,8 @@ static const struct ctl_table events_core_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_perf_cpu_time_max_percent),
 		.mode		= 0644,
 		.proc_handler	= perf_cpu_time_max_percent_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE_HUNDRED,
+		.min		= 0,
+		.max		= 100,
 	},
 };
 
diff --git a/kernel/hung_task.c b/kernel/hung_task.c
index b8554aedc589..88e260d623ff 100644
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/mm.h>
+#include <linux/limits.h>
 #include <linux/cpu.h>
 #include <linux/nmi.h>
 #include <linux/init.h>
@@ -462,11 +463,6 @@ static int proc_dohung_task_warnings(const struct ctl_table *table, int write,
 	return ret;
 }
 
-/*
- * This is needed for proc_doulongvec_minmax of sysctl_hung_task_timeout_secs
- * and hung_task_check_interval_secs
- */
-static const unsigned long hung_task_timeout_max = (LONG_MAX / HZ);
 static const struct ctl_table hung_task_sysctls[] = {
 #ifdef CONFIG_SMP
 	{
@@ -475,8 +471,8 @@ static const struct ctl_table hung_task_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #endif /* CONFIG_SMP */
 	{
@@ -485,8 +481,8 @@ static const struct ctl_table hung_task_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "hung_task_check_count",
@@ -494,7 +490,8 @@ static const struct ctl_table hung_task_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "hung_task_timeout_secs",
@@ -502,7 +499,9 @@ static const struct ctl_table hung_task_sysctls[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_dohung_task_timeout_secs,
-		.extra2		= (void *)&hung_task_timeout_max,
+		.min		= 0,
+		/* Keep the timeout's jiffies conversion within a signed long. */
+		.max		= LONG_MAX / HZ,
 	},
 	{
 		.procname	= "hung_task_check_interval_secs",
@@ -510,7 +509,9 @@ static const struct ctl_table hung_task_sysctls[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_dohung_task_timeout_secs,
-		.extra2		= (void *)&hung_task_timeout_max,
+		.min		= 0,
+		/* Keep the timeout's jiffies conversion within a signed long. */
+		.max		= LONG_MAX / HZ,
 	},
 	{
 		.procname	= "hung_task_warnings",
@@ -518,13 +519,16 @@ static const struct ctl_table hung_task_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dohung_task_warnings,
-		.extra1		= SYSCTL_NEG_ONE,
+		.min		= (unsigned long)-1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "hung_task_detect_count",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_dohung_task_detect_count,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "hung_task_sys_info",
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index f4a31565c447..618aa15f27f5 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -1097,8 +1097,8 @@ static const struct ctl_table kexec_core_sysctls[] = {
 		.mode		= 0644,
 		/* only handle a transition from default "0" to "1" */
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= 1,
 	},
 	{
 		.procname	= "kexec_load_limit_panic",
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 2441b9e39343..b098d6a7c89b 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -1242,8 +1242,8 @@ static const struct ctl_table kprobe_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_kprobes_optimization_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/kernel/kstack_erase.c b/kernel/kstack_erase.c
index d4449884084c..e44d6e626aec 100644
--- a/kernel/kstack_erase.c
+++ b/kernel/kstack_erase.c
@@ -52,8 +52,8 @@ static const struct ctl_table stackleak_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0600,
 		.proc_handler	= stack_erasing_sysctl,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 34e0204ea37c..7b101cb0d918 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -146,8 +146,8 @@ static const struct ctl_table module_sysctl_table[] = {
 		.mode		= 0644,
 		/* only handle a transition from default "0" to "1" */
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= 1,
 	},
 };
 
diff --git a/kernel/panic.c b/kernel/panic.c
index b824b68fcb08..901322b1eacf 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -10,6 +10,7 @@
  * to indicate a major problem.
  */
 #include <linux/debug_locks.h>
+#include <linux/limits.h>
 #include <linux/sched/debug.h>
 #include <linux/interrupt.h>
 #include <linux/kgdb.h>
@@ -148,8 +149,8 @@ static const struct ctl_table kern_panic_table[] = {
 		.maxlen         = sizeof(int),
 		.mode           = 0644,
 		.proc_handler   = proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min            = 0,
+		.max            = 1,
 	},
 #endif
 	{
@@ -157,6 +158,8 @@ static const struct ctl_table kern_panic_table[] = {
 		.maxlen		= sizeof(long),
 		.mode		= 0644,
 		.proc_handler	= proc_taint,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "panic",
@@ -178,6 +181,8 @@ static const struct ctl_table kern_panic_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= sysctl_panic_print_handler,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "panic_on_warn",
@@ -185,8 +190,8 @@ static const struct ctl_table kern_panic_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname       = "warn_limit",
diff --git a/kernel/printk/sysctl.c b/kernel/printk/sysctl.c
index f15732e93c2e..79b1fc60aa91 100644
--- a/kernel/printk/sysctl.c
+++ b/kernel/printk/sysctl.c
@@ -8,10 +8,8 @@
 #include <linux/ratelimit.h>
 #include "internal.h"
 
-static const int ten_thousand = 10000;
-
-static int proc_dointvec_minmax_sysadmin(const struct ctl_table *table, int write,
-				void *buffer, size_t *lenp, loff_t *ppos)
+static int proc_dointvec_range_sysadmin(const struct ctl_table *table, int write,
+					void *buffer, size_t *lenp, loff_t *ppos)
 {
 	if (write && !capable(CAP_SYS_ADMIN))
 		return -EPERM;
@@ -47,8 +45,8 @@ static const struct ctl_table printk_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= (void *)&ten_thousand,
+		.min		= 0,
+		.max		= 10000,
 	},
 	{
 		.procname	= "printk_devkmsg",
@@ -62,18 +60,18 @@ static const struct ctl_table printk_sysctls[] = {
 		.data		= &dmesg_restrict,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax_sysadmin,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.proc_handler	= proc_dointvec_range_sysadmin,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "kptr_restrict",
 		.data		= &kptr_restrict,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax_sysadmin,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.proc_handler	= proc_dointvec_range_sysadmin,
+		.min		= 0,
+		.max		= 2,
 	},
 };
 
diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h
index 803a56af3259..e5a230514a86 100644
--- a/kernel/rcu/tree_stall.h
+++ b/kernel/rcu/tree_stall.h
@@ -8,6 +8,7 @@
  */
 
 #include <linux/console.h>
+#include <linux/limits.h>
 #include <linux/kvm_para.h>
 #include <linux/rcu_notifier.h>
 #include <linux/smp.h>
@@ -29,8 +30,8 @@ static const struct ctl_table rcu_stall_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_panic_on_rcu_stall),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "max_rcu_stall_to_panic",
@@ -38,8 +39,8 @@ static const struct ctl_table rcu_stall_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_max_rcu_stall_to_panic),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 };
 
diff --git a/kernel/sched/autogroup.c b/kernel/sched/autogroup.c
index e380cf9372bb..3198fe47efb8 100644
--- a/kernel/sched/autogroup.c
+++ b/kernel/sched/autogroup.c
@@ -19,8 +19,8 @@ static const struct ctl_table sched_autogroup_sysctls[] = {
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
 		.proc_handler   = proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min            = 0,
+		.max            = 1,
 	},
 };
 
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 04b62c16fa8c..293f4cc423ca 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -4823,8 +4823,8 @@ static const struct ctl_table sched_core_sysctls[] = {
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
 		.proc_handler   = sysctl_schedstats,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min            = 0,
+		.max            = 1,
 	},
 #endif /* CONFIG_SCHEDSTATS */
 #ifdef CONFIG_UCLAMP_TASK
@@ -4857,8 +4857,8 @@ static const struct ctl_table sched_core_sysctls[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= sysctl_numa_balancing,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_FOUR,
+		.min		= 0,
+		.max		= 4,
 	},
 #endif /* CONFIG_NUMA_BALANCING */
 };
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index cfc982205b98..3956f35927bd 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -145,7 +145,8 @@ static const struct ctl_table sched_fair_sysctls[] = {
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
 		.proc_handler   = proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
+		.min            = 1,
+		.max            = INT_MAX,
 	},
 #endif
 #ifdef CONFIG_NUMA_BALANCING
@@ -155,7 +156,8 @@ static const struct ctl_table sched_fair_sysctls[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 #endif /* CONFIG_NUMA_BALANCING */
 };
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 919d0fb00bd9..caaba36abdd4 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -319,8 +319,8 @@ static const struct ctl_table sched_energy_aware_sysctls[] = {
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
 		.proc_handler   = sched_energy_aware_handler,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min            = 0,
+		.max            = 1,
 	},
 };
 
diff --git a/kernel/sys.c b/kernel/sys.c
index 755bfa5ccd92..676ffca63a05 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -188,8 +188,8 @@ static const struct ctl_table overflow_sysctl_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_MAXOLDUID,
+		.min		= 0,
+		.max		= 65535,
 	},
 	{
 		.procname	= "overflowgid",
@@ -197,8 +197,8 @@ static const struct ctl_table overflow_sysctl_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_MAXOLDUID,
+		.min		= 0,
+		.max		= 65535,
 	},
 };
 
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index a3c4e20a4e36..8cb0c3cad455 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -309,8 +309,8 @@ static const struct ctl_table timer_sysctl[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= timer_migration_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/kernel/ucount.c b/kernel/ucount.c
index ec8b1445e287..8c7738f240ec 100644
--- a/kernel/ucount.c
+++ b/kernel/ucount.c
@@ -61,17 +61,14 @@ static struct ctl_table_root set_root = {
 	.permissions = set_permissions,
 };
 
-static long ue_zero = 0;
-static long ue_int_max = INT_MAX;
-
 #define UCOUNT_ENTRY(name)					\
 	{							\
 		.procname	= name,				\
 		.maxlen		= sizeof(long),			\
 		.mode		= 0644,				\
 		.proc_handler	= proc_doulongvec_minmax,	\
-		.extra1		= &ue_zero,			\
-		.extra2		= &ue_int_max,			\
+		.min		= 0,				\
+		.max		= INT_MAX,			\
 	}
 static const struct ctl_table user_table[] = {
 	UCOUNT_ENTRY("max_user_namespaces"),
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 64da2e500144..f440522707ec 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -13,6 +13,7 @@
 #define pr_fmt(fmt) "watchdog: " fmt
 
 #include <linux/cpu.h>
+#include <linux/limits.h>
 #include <linux/init.h>
 #include <linux/irq.h>
 #include <linux/irqdesc.h>
@@ -1213,8 +1214,6 @@ static int proc_watchdog_cpumask(const struct ctl_table *table, int write,
 	return err;
 }
 
-static const int sixty = 60;
-
 static const struct ctl_table watchdog_sysctls[] = {
 	{
 		.procname       = "watchdog",
@@ -1222,8 +1221,8 @@ static const struct ctl_table watchdog_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler   = proc_watchdog,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "watchdog_thresh",
@@ -1231,8 +1230,8 @@ static const struct ctl_table watchdog_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_watchdog_thresh,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= (void *)&sixty,
+		.min		= 0,
+		.max		= 60,
 	},
 	{
 		.procname	= "watchdog_cpumask",
@@ -1248,8 +1247,8 @@ static const struct ctl_table watchdog_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler   = proc_soft_watchdog,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "softlockup_panic",
@@ -1257,8 +1256,8 @@ static const struct ctl_table watchdog_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "softlockup_sys_info",
@@ -1274,8 +1273,8 @@ static const struct ctl_table watchdog_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #endif /* CONFIG_SMP */
 #endif
@@ -1286,8 +1285,8 @@ static const struct ctl_table watchdog_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "hardlockup_sys_info",
@@ -1303,8 +1302,8 @@ static const struct ctl_table watchdog_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #endif /* CONFIG_SMP */
 #endif
@@ -1314,8 +1313,8 @@ static const struct ctl_table watchdog_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler   = proc_nmi_watchdog,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
-- 
2.25.1


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

* [RFC PATCH 13/39] sysctl: store memory-management sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (11 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 12/39] sysctl: store kernel " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 14/39] sysctl: store IPv6 scalar " wen.yang
                   ` (25 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Use numeric memory-management bounds, refreshing mmap limits at runtime.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 mm/compaction.c     | 19 ++++++++++---------
 mm/filemap.c        |  4 +++-
 mm/memory-failure.c | 16 ++++++++--------
 mm/mmap.c           | 28 +++++++++++++++++++++-------
 mm/nommu.c          |  4 +++-
 mm/oom_kill.c       |  4 ++--
 mm/page-writeback.c | 25 +++++++++++++------------
 mm/page_alloc.c     | 28 ++++++++++++++++------------
 mm/swap_state.c     |  5 ++---
 mm/userfaultfd.c    |  4 ++--
 mm/util.c           |  7 ++++---
 mm/vmscan.c         |  8 +++++---
 mm/vmstat.c         |  4 ++--
 13 files changed, 91 insertions(+), 65 deletions(-)

diff --git a/mm/compaction.c b/mm/compaction.c
index f1b2060eb201..6c3a3d08e42a 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -3296,8 +3296,9 @@ void __meminit kcompactd_stop(int nid)
 	}
 }
 
-static int proc_dointvec_minmax_warn_RT_change(const struct ctl_table *table,
-		int write, void *buffer, size_t *lenp, loff_t *ppos)
+static int proc_dointvec_range_warn_RT_change(const struct ctl_table *table,
+					      int write, void *buffer,
+					      size_t *lenp, loff_t *ppos)
 {
 	int ret, old;
 
@@ -3329,8 +3330,8 @@ static const struct ctl_table vm_compaction[] = {
 		.maxlen		= sizeof(sysctl_compaction_proactiveness),
 		.mode		= 0644,
 		.proc_handler	= compaction_proactiveness_sysctl_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE_HUNDRED,
+		.min		= 0,
+		.max		= 100,
 	},
 	{
 		.procname	= "extfrag_threshold",
@@ -3338,17 +3339,17 @@ static const struct ctl_table vm_compaction[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE_THOUSAND,
+		.min		= 0,
+		.max		= 1000,
 	},
 	{
 		.procname	= "compact_unevictable_allowed",
 		.data		= &sysctl_compact_unevictable_allowed,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax_warn_RT_change,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.proc_handler	= proc_dointvec_range_warn_RT_change,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/mm/filemap.c b/mm/filemap.c
index b74bc1e5015c..b465bfee3782 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -11,6 +11,7 @@
  * the NFS filesystem used to do this differently, for example)
  */
 #include <linux/export.h>
+#include <linux/limits.h>
 #include <linux/compiler.h>
 #include <linux/dax.h>
 #include <linux/fs.h>
@@ -1086,7 +1087,8 @@ static const struct ctl_table filemap_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_page_lock_unfairness),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	}
 };
 
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index b705f9f97f42..5c5c7ec329b7 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -178,8 +178,8 @@ static const struct ctl_table memory_failure_table[] = {
 		.maxlen		= sizeof(sysctl_memory_failure_early_kill),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "memory_failure_recovery",
@@ -187,8 +187,8 @@ static const struct ctl_table memory_failure_table[] = {
 		.maxlen		= sizeof(sysctl_memory_failure_recovery),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "enable_soft_offline",
@@ -196,8 +196,8 @@ static const struct ctl_table memory_failure_table[] = {
 		.maxlen		= sizeof(sysctl_enable_soft_offline),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "panic_on_unrecoverable_memory_failure",
@@ -205,8 +205,8 @@ static const struct ctl_table memory_failure_table[] = {
 		.maxlen		= sizeof(sysctl_panic_on_unrecoverable_mf),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	}
 };
 
diff --git a/mm/mmap.c b/mm/mmap.c
index 148ba01c0373..bc1ad6431bf9 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -10,6 +10,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/kernel.h>
+#include <linux/limits.h>
 #include <linux/slab.h>
 #include <linux/backing-dev.h>
 #include <linux/mm.h>
@@ -1493,6 +1494,17 @@ struct vm_area_struct *_install_special_mapping(
 int sysctl_legacy_va_layout;
 #endif
 
+#ifdef CONFIG_HAVE_ARCH_MMAP_RND_BITS
+static int mmap_rnd_bits_handler(const struct ctl_table *table, int write,
+				 void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.max = mmap_rnd_bits_max;
+	return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+#endif
+
 static const struct ctl_table mmap_table[] = {
 		{
 				.procname       = "max_map_count",
@@ -1500,7 +1512,8 @@ static const struct ctl_table mmap_table[] = {
 				.maxlen         = sizeof(sysctl_max_map_count),
 				.mode           = 0644,
 				.proc_handler   = proc_dointvec_minmax,
-				.extra1         = SYSCTL_ZERO,
+				.min            = 0,
+				.max            = INT_MAX,
 		},
 #if defined(HAVE_ARCH_PICK_MMAP_LAYOUT) || \
 		defined(CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT)
@@ -1510,7 +1523,8 @@ static const struct ctl_table mmap_table[] = {
 				.maxlen         = sizeof(sysctl_legacy_va_layout),
 				.mode           = 0644,
 				.proc_handler   = proc_dointvec_minmax,
-				.extra1         = SYSCTL_ZERO,
+				.min            = 0,
+				.max            = INT_MAX,
 		},
 #endif
 #ifdef CONFIG_HAVE_ARCH_MMAP_RND_BITS
@@ -1519,9 +1533,9 @@ static const struct ctl_table mmap_table[] = {
 				.data           = &mmap_rnd_bits,
 				.maxlen         = sizeof(mmap_rnd_bits),
 				.mode           = 0600,
-				.proc_handler   = proc_dointvec_minmax,
-				.extra1         = (void *)&mmap_rnd_bits_min,
-				.extra2         = (void *)&mmap_rnd_bits_max,
+				.proc_handler   = mmap_rnd_bits_handler,
+				.min            = CONFIG_ARCH_MMAP_RND_BITS_MIN,
+				.max            = CONFIG_ARCH_MMAP_RND_BITS_MAX,
 		},
 #endif
 #ifdef CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS
@@ -1531,8 +1545,8 @@ static const struct ctl_table mmap_table[] = {
 				.maxlen         = sizeof(mmap_rnd_compat_bits),
 				.mode           = 0600,
 				.proc_handler   = proc_dointvec_minmax,
-				.extra1         = (void *)&mmap_rnd_compat_bits_min,
-				.extra2         = (void *)&mmap_rnd_compat_bits_max,
+				.min            = CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN,
+				.max            = CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX,
 		},
 #endif
 };
diff --git a/mm/nommu.c b/mm/nommu.c
index bc87946e5cc2..3c1a7a9765a5 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -17,6 +17,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/export.h>
+#include <linux/limits.h>
 #include <linux/mm.h>
 #include <linux/sched/mm.h>
 #include <linux/mman.h>
@@ -411,7 +412,8 @@ static const struct ctl_table nommu_table[] = {
 		.maxlen		= sizeof(sysctl_nr_trim_pages),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 };
 
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index 5d48bd862c27..6bb4e5a1b419 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -696,8 +696,8 @@ static const struct ctl_table vm_oom_kill_table[] = {
 		.maxlen		= sizeof(sysctl_panic_on_oom),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname	= "oom_kill_allocating_task",
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 0694ec4098f6..40fdc2d698d9 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -13,6 +13,7 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/limits.h>
 #include <linux/math64.h>
 #include <linux/export.h>
 #include <linux/spinlock.h>
@@ -2227,9 +2228,6 @@ static int laptop_mode_handler(const struct ctl_table *table, int write,
 	return ret;
 }
 
-/* this is needed for the proc_doulongvec_minmax of vm_dirty_bytes */
-static const unsigned long dirty_bytes_min = 2 * PAGE_SIZE;
-
 static const struct ctl_table vm_page_writeback_sysctls[] = {
 	{
 		.procname   = "dirty_background_ratio",
@@ -2237,8 +2235,8 @@ static const struct ctl_table vm_page_writeback_sysctls[] = {
 		.maxlen     = sizeof(dirty_background_ratio),
 		.mode       = 0644,
 		.proc_handler   = dirty_background_ratio_handler,
-		.extra1     = SYSCTL_ZERO,
-		.extra2     = SYSCTL_ONE_HUNDRED,
+		.min            = 0,
+		.max            = 100,
 	},
 	{
 		.procname   = "dirty_background_bytes",
@@ -2246,7 +2244,8 @@ static const struct ctl_table vm_page_writeback_sysctls[] = {
 		.maxlen     = sizeof(dirty_background_bytes),
 		.mode       = 0644,
 		.proc_handler   = dirty_background_bytes_handler,
-		.extra1     = SYSCTL_LONG_ONE,
+		.min            = 1,
+		.max            = ULONG_MAX,
 	},
 	{
 		.procname   = "dirty_ratio",
@@ -2254,8 +2253,8 @@ static const struct ctl_table vm_page_writeback_sysctls[] = {
 		.maxlen     = sizeof(vm_dirty_ratio),
 		.mode       = 0644,
 		.proc_handler   = dirty_ratio_handler,
-		.extra1     = SYSCTL_ZERO,
-		.extra2     = SYSCTL_ONE_HUNDRED,
+		.min            = 0,
+		.max            = 100,
 	},
 	{
 		.procname   = "dirty_bytes",
@@ -2263,7 +2262,8 @@ static const struct ctl_table vm_page_writeback_sysctls[] = {
 		.maxlen     = sizeof(vm_dirty_bytes),
 		.mode       = 0644,
 		.proc_handler   = dirty_bytes_handler,
-		.extra1     = (void *)&dirty_bytes_min,
+		.min            = 2 * PAGE_SIZE,
+		.max            = ULONG_MAX,
 	},
 	{
 		.procname   = "dirty_writeback_centisecs",
@@ -2278,7 +2278,8 @@ static const struct ctl_table vm_page_writeback_sysctls[] = {
 		.maxlen     = sizeof(dirty_expire_interval),
 		.mode       = 0644,
 		.proc_handler   = proc_dointvec_minmax,
-		.extra1     = SYSCTL_ZERO,
+		.min            = 0,
+		.max            = INT_MAX,
 	},
 #ifdef CONFIG_HIGHMEM
 	{
@@ -2287,8 +2288,8 @@ static const struct ctl_table vm_page_writeback_sysctls[] = {
 		.maxlen		= sizeof(vm_highmem_is_dirtyable),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #endif
 	{
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 2b2855300217..956acbe7e5bd 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -6933,7 +6933,8 @@ static const struct ctl_table page_alloc_sysctl_table[] = {
 		.maxlen		= sizeof(min_free_kbytes),
 		.mode		= 0644,
 		.proc_handler	= min_free_kbytes_sysctl_handler,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "watermark_boost_factor",
@@ -6941,7 +6942,8 @@ static const struct ctl_table page_alloc_sysctl_table[] = {
 		.maxlen		= sizeof(watermark_boost_factor),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "watermark_scale_factor",
@@ -6949,8 +6951,8 @@ static const struct ctl_table page_alloc_sysctl_table[] = {
 		.maxlen		= sizeof(watermark_scale_factor),
 		.mode		= 0644,
 		.proc_handler	= watermark_scale_factor_sysctl_handler,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_THREE_THOUSAND,
+		.min		= 1,
+		.max		= 3000,
 	},
 	{
 		.procname	= "defrag_mode",
@@ -6958,8 +6960,8 @@ static const struct ctl_table page_alloc_sysctl_table[] = {
 		.maxlen		= sizeof(defrag_mode),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "percpu_pagelist_high_fraction",
@@ -6967,7 +6969,8 @@ static const struct ctl_table page_alloc_sysctl_table[] = {
 		.maxlen		= sizeof(percpu_pagelist_high_fraction),
 		.mode		= 0644,
 		.proc_handler	= percpu_pagelist_high_fraction_sysctl_handler,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "lowmem_reserve_ratio",
@@ -6975,7 +6978,8 @@ static const struct ctl_table page_alloc_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_lowmem_reserve_ratio),
 		.mode		= 0644,
 		.proc_handler	= lowmem_reserve_ratio_sysctl_handler,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 #ifdef CONFIG_NUMA
 	{
@@ -6991,8 +6995,8 @@ static const struct ctl_table page_alloc_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_min_unmapped_ratio),
 		.mode		= 0644,
 		.proc_handler	= sysctl_min_unmapped_ratio_sysctl_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE_HUNDRED,
+		.min		= 0,
+		.max		= 100,
 	},
 	{
 		.procname	= "min_slab_ratio",
@@ -7000,8 +7004,8 @@ static const struct ctl_table page_alloc_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_min_slab_ratio),
 		.mode		= 0644,
 		.proc_handler	= sysctl_min_slab_ratio_sysctl_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE_HUNDRED,
+		.min		= 0,
+		.max		= 100,
 	},
 #endif
 };
diff --git a/mm/swap_state.c b/mm/swap_state.c
index d9e36c505398..1097ba48b8b1 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -30,7 +30,6 @@
 
 /* Swap readahead cluster size, as a power of 2 pages. */
 static int page_cluster;
-static const int page_cluster_max = 31;
 
 /*
  * swapper_space is a fiction, retained to simplify the path through
@@ -1071,8 +1070,8 @@ static const struct ctl_table swap_readahead_sysctl_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= (void *)&page_cluster_max,
+		.min		= 0,
+		.max		= 31,
 	}
 };
 
diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
index 3b71712edab4..f631a4e27aae 100644
--- a/mm/userfaultfd.c
+++ b/mm/userfaultfd.c
@@ -2405,8 +2405,8 @@ static const struct ctl_table vm_userfaultfd_table[] = {
 		.maxlen		= sizeof(sysctl_unprivileged_userfaultfd),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 #endif
diff --git a/mm/util.c b/mm/util.c
index 2f2bc521868a..1460f47c10c5 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -1,6 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-only
 #include <linux/mm.h>
-#include <linux/limits.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/compiler.h>
@@ -858,8 +857,8 @@ static const struct ctl_table util_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_overcommit_memory),
 		.mode		= 0644,
 		.proc_handler	= overcommit_policy_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname	= "overcommit_ratio",
@@ -874,6 +873,8 @@ static const struct ctl_table util_sysctl_table[] = {
 		.maxlen		= sizeof(sysctl_overcommit_kbytes),
 		.mode		= 0644,
 		.proc_handler	= overcommit_kbytes_handler,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "user_reserve_kbytes",
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 9959ca158ce8..48b7b373a76c 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -13,6 +13,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/mm.h>
+#include <linux/limits.h>
 #include <linux/sched/mm.h>
 #include <linux/module.h>
 #include <linux/gfp.h>
@@ -7884,8 +7885,8 @@ static const struct ctl_table vmscan_sysctl_table[] = {
 		.maxlen		= sizeof(vm_swappiness),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO_HUNDRED,
+		.min		= 0,
+		.max		= 200,
 	},
 #ifdef CONFIG_NUMA
 	{
@@ -7894,7 +7895,8 @@ static const struct ctl_table vmscan_sysctl_table[] = {
 		.maxlen		= sizeof(node_reclaim_mode),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	}
 #endif
 };
diff --git a/mm/vmstat.c b/mm/vmstat.c
index a3e809c57f29..4d0a44624d16 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -2279,8 +2279,8 @@ static const struct ctl_table vmstat_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= sysctl_vm_numa_stat_handler,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #endif
 };
-- 
2.25.1


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

* [RFC PATCH 14/39] sysctl: store IPv6 scalar sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (12 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 13/39] sysctl: store memory-management " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 15/39] sysctl: store netfilter " wen.yang
                   ` (24 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Use numeric IPv6 bounds while preserving the IOAM native-word behavior.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/ipv6/icmp.c            | 22 ++++++++++-----------
 net/ipv6/sysctl_net_ipv6.c | 40 ++++++++++++++++++++------------------
 2 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 1ba2ff759075..4a38b2046876 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -1372,8 +1372,6 @@ EXPORT_SYMBOL(icmpv6_err_convert);
 
 #ifdef CONFIG_SYSCTL
 
-static u32 icmpv6_errors_extension_mask_all =
-	GENMASK_U8(ICMP_ERR_EXT_COUNT - 1, 0);
 
 static const struct ctl_table ipv6_icmp_table_template[] = {
 	{
@@ -1389,8 +1387,8 @@ static const struct ctl_table ipv6_icmp_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler = proc_dou8vec_minmax,
-		.min		= 0,
-		.max		= U8_MAX,
+		.min          = 0,
+		.max          = U8_MAX,
 	},
 	{
 		.procname	= "echo_ignore_multicast",
@@ -1398,8 +1396,8 @@ static const struct ctl_table ipv6_icmp_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler = proc_dou8vec_minmax,
-		.min		= 0,
-		.max		= U8_MAX,
+		.min          = 0,
+		.max          = U8_MAX,
 	},
 	{
 		.procname	= "echo_ignore_anycast",
@@ -1407,8 +1405,8 @@ static const struct ctl_table ipv6_icmp_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler = proc_dou8vec_minmax,
-		.min		= 0,
-		.max		= U8_MAX,
+		.min          = 0,
+		.max          = U8_MAX,
 	},
 	{
 		.procname	= "ratemask",
@@ -1423,8 +1421,8 @@ static const struct ctl_table ipv6_icmp_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "errors_extension_mask",
@@ -1432,8 +1430,8 @@ static const struct ctl_table ipv6_icmp_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &icmpv6_errors_extension_mask_all,
+		.min		= 0,
+		.max		= GENMASK_U8(ICMP_ERR_EXT_COUNT - 1, 0),
 	},
 };
 
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index c870a364de60..144bbc1ae0c8 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -24,13 +24,6 @@
 #endif
 #include <linux/ioam6.h>
 
-static int flowlabel_reflect_max = 0x7;
-static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX;
-static u32 rt6_multipath_hash_fields_all_mask =
-	FIB_MULTIPATH_HASH_FIELD_ALL_MASK;
-static u32 ioam6_id_max = IOAM6_DEFAULT_ID;
-static u64 ioam6_id_wide_max = IOAM6_DEFAULT_ID_WIDE;
-
 static int proc_rt6_multipath_hash_policy(const struct ctl_table *table, int write,
 					  void *buffer, size_t *lenp, loff_t *ppos)
 {
@@ -96,7 +89,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra2		= &auto_flowlabels_max
+		.min		= 0,
+		.max		= IP6_AUTO_FLOW_LABEL_MAX,
 	},
 	{
 		.procname	= "fwmark_reflect",
@@ -145,8 +139,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &flowlabel_reflect_max,
+		.min		= 0,
+		.max		= 0x7,
 	},
 	{
 		.procname	= "max_dst_opts_number",
@@ -182,8 +176,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler   = proc_rt6_multipath_hash_policy,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_THREE,
+		.min		= 0,
+		.max		= 3,
 	},
 	{
 		.procname	= "fib_multipath_hash_fields",
@@ -191,8 +185,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u32),
 		.mode		= 0644,
 		.proc_handler	= proc_rt6_multipath_hash_fields,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &rt6_multipath_hash_fields_all_mask,
+		.min		= 1,
+		.max		= FIB_MULTIPATH_HASH_FIELD_ALL_MASK,
 	},
 	{
 		.procname	= "seg6_flowlabel",
@@ -207,8 +201,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname	= "ioam6_id",
@@ -216,7 +210,8 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u32),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra2		= &ioam6_id_max,
+		.min		= 0,
+		.max		= IOAM6_DEFAULT_ID,
 	},
 	{
 		.procname	= "ioam6_id_wide",
@@ -224,7 +219,13 @@ static const struct ctl_table ipv6_table_template[] = {
 		.maxlen		= sizeof(u64),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra2		= &ioam6_id_wide_max,
+		.min		= 0,
+		/* Preserve the native unsigned long bound read from the u64. */
+#if BITS_PER_LONG == 32 && defined(__BIG_ENDIAN)
+		.max		= (unsigned long)(IOAM6_DEFAULT_ID_WIDE >> 32),
+#else
+		.max		= (unsigned long)IOAM6_DEFAULT_ID_WIDE,
+#endif
 	},
 };
 
@@ -242,7 +243,8 @@ static struct ctl_table ipv6_rotable[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 #ifdef CONFIG_NETLABEL
 	{
-- 
2.25.1


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

* [RFC PATCH 15/39] sysctl: store netfilter scalar sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (13 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 14/39] sysctl: store IPv6 scalar " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 16/39] sysctl: store RPC " wen.yang
                   ` (23 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Replace netfilter bound pointers with numeric limits.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/netfilter/nf_conntrack_standalone.c | 42 +++++++++++++------------
 net/netfilter/nf_hooks_lwtunnel.c       |  4 +--
 2 files changed, 24 insertions(+), 22 deletions(-)

diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 561db3aecada..65f0c3ab3f2b 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -647,8 +647,8 @@ static const struct ctl_table nf_ct_sysctl_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	[NF_SYSCTL_CT_COUNT] = {
 		.procname	= "nf_conntrack_count",
@@ -669,8 +669,8 @@ static const struct ctl_table nf_ct_sysctl_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2 	= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	[NF_SYSCTL_CT_LOG_INVALID] = {
 		.procname	= "nf_conntrack_log_invalid",
@@ -678,6 +678,8 @@ static const struct ctl_table nf_ct_sysctl_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= nf_conntrack_log_invalid_sysctl,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	[NF_SYSCTL_CT_EXPECT_MAX] = {
 		.procname	= "nf_conntrack_expect_max",
@@ -685,8 +687,8 @@ static const struct ctl_table nf_ct_sysctl_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	[NF_SYSCTL_CT_ACCT] = {
 		.procname	= "nf_conntrack_acct",
@@ -694,8 +696,8 @@ static const struct ctl_table nf_ct_sysctl_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2 	= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
 	[NF_SYSCTL_CT_EVENTS] = {
@@ -704,8 +706,8 @@ static const struct ctl_table nf_ct_sysctl_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 #endif
 #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
@@ -715,8 +717,8 @@ static const struct ctl_table nf_ct_sysctl_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2 	= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #endif
 	[NF_SYSCTL_CT_PROTO_TIMEOUT_GENERIC] = {
@@ -798,24 +800,24 @@ static const struct ctl_table nf_ct_sysctl_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2 	= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	[NF_SYSCTL_CT_PROTO_TCP_LIBERAL] = {
 		.procname       = "nf_conntrack_tcp_be_liberal",
 		.maxlen		= sizeof(u8),
 		.mode           = 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1 	= SYSCTL_ZERO,
-		.extra2 	= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	[NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST] = {
 		.procname	= "nf_conntrack_tcp_ignore_invalid_rst",
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	[NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS] = {
 		.procname	= "nf_conntrack_tcp_max_retrans",
@@ -930,8 +932,8 @@ static struct ctl_table nf_ct_netfilter_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 };
 
diff --git a/net/netfilter/nf_hooks_lwtunnel.c b/net/netfilter/nf_hooks_lwtunnel.c
index 40c6fce14a63..93339b735dbb 100644
--- a/net/netfilter/nf_hooks_lwtunnel.c
+++ b/net/netfilter/nf_hooks_lwtunnel.c
@@ -37,8 +37,8 @@ int nf_hooks_lwtunnel_sysctl_handler(const struct ctl_table *table, int write,
 		.data = &proc_nf_hooks_lwtunnel_enabled,
 		.maxlen = sizeof(int),
 		.mode = table->mode,
-		.extra1 = SYSCTL_ZERO,
-		.extra2 = SYSCTL_ONE,
+		.min    = 0,
+		.max    = 1,
 	};
 	int ret;
 
-- 
2.25.1


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

* [RFC PATCH 16/39] sysctl: store RPC sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (14 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 15/39] sysctl: store netfilter " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 17/39] sysctl: store remaining network " wen.yang
                   ` (22 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Replace RPC bound pointers with numeric limits.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/sunrpc/xprtrdma/svc_rdma.c  | 39 ++++++++++++++-------------------
 net/sunrpc/xprtrdma/transport.c | 27 +++++++++--------------
 net/sunrpc/xprtsock.c           | 26 +++++++++-------------
 3 files changed, 36 insertions(+), 56 deletions(-)

diff --git a/net/sunrpc/xprtrdma/svc_rdma.c b/net/sunrpc/xprtrdma/svc_rdma.c
index f67f0612b1a9..0ba54bf6bc7c 100644
--- a/net/sunrpc/xprtrdma/svc_rdma.c
+++ b/net/sunrpc/xprtrdma/svc_rdma.c
@@ -53,17 +53,10 @@
 
 /* RPC/RDMA parameters */
 unsigned int svcrdma_ord = 16;	/* historical default */
-static unsigned int min_ord = 1;
-static unsigned int max_ord = 255;
 unsigned int svcrdma_max_requests = RPCRDMA_MAX_REQUESTS;
 unsigned int svcrdma_max_bc_requests = RPCRDMA_MAX_BC_REQUESTS;
-static unsigned int min_max_requests = 4;
-static unsigned int max_max_requests = 16384;
 unsigned int svcrdma_max_req_size = RPCRDMA_DEF_INLINE_THRESH;
-static unsigned int min_max_inline = RPCRDMA_DEF_INLINE_THRESH;
-static unsigned int max_max_inline = RPCRDMA_MAX_INLINE_THRESH;
 static unsigned int svcrdma_stat_unused;
-static unsigned int zero;
 
 struct percpu_counter svcrdma_stat_read;
 struct percpu_counter svcrdma_stat_recv;
@@ -114,8 +107,8 @@ static struct ctl_table svcrdma_parm_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_max_requests,
-		.extra2		= &max_max_requests
+		.min		= 4,
+		.max		= 16384
 	},
 	{
 		.procname	= "max_req_size",
@@ -123,8 +116,8 @@ static struct ctl_table svcrdma_parm_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_max_inline,
-		.extra2		= &max_max_inline
+		.min		= RPCRDMA_DEF_INLINE_THRESH,
+		.max		= RPCRDMA_MAX_INLINE_THRESH
 	},
 	{
 		.procname	= "max_outbound_read_requests",
@@ -132,8 +125,8 @@ static struct ctl_table svcrdma_parm_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_ord,
-		.extra2		= &max_ord,
+		.min		= 1,
+		.max		= 255,
 	},
 
 	{
@@ -170,8 +163,8 @@ static struct ctl_table svcrdma_parm_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &zero,
-		.extra2		= &zero,
+		.min		= 0,
+		.max		= 0,
 	},
 	{
 		.procname	= "rdma_stat_rq_poll",
@@ -179,8 +172,8 @@ static struct ctl_table svcrdma_parm_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &zero,
-		.extra2		= &zero,
+		.min		= 0,
+		.max		= 0,
 	},
 	{
 		.procname	= "rdma_stat_rq_prod",
@@ -188,8 +181,8 @@ static struct ctl_table svcrdma_parm_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &zero,
-		.extra2		= &zero,
+		.min		= 0,
+		.max		= 0,
 	},
 	{
 		.procname	= "rdma_stat_sq_poll",
@@ -197,8 +190,8 @@ static struct ctl_table svcrdma_parm_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &zero,
-		.extra2		= &zero,
+		.min		= 0,
+		.max		= 0,
 	},
 	{
 		.procname	= "rdma_stat_sq_prod",
@@ -206,8 +199,8 @@ static struct ctl_table svcrdma_parm_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &zero,
-		.extra2		= &zero,
+		.min		= 0,
+		.max		= 0,
 	},
 };
 
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c
index d4e6746d8ecd..ef84dde20228 100644
--- a/net/sunrpc/xprtrdma/transport.c
+++ b/net/sunrpc/xprtrdma/transport.c
@@ -73,13 +73,6 @@ static struct xprt_class xprt_rdma;
 
 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
 
-static unsigned int min_slot_table_size = RPCRDMA_MIN_SLOT_TABLE;
-static unsigned int max_slot_table_size = RPCRDMA_MAX_SLOT_TABLE;
-static unsigned int min_inline_size = RPCRDMA_MIN_INLINE;
-static unsigned int max_inline_size = RPCRDMA_MAX_INLINE;
-static unsigned int max_padding = PAGE_SIZE;
-static unsigned int min_memreg = RPCRDMA_BOUNCEBUFFERS;
-static unsigned int max_memreg = RPCRDMA_LAST - 1;
 static unsigned int dummy;
 
 static struct ctl_table_header *sunrpc_table_header;
@@ -91,8 +84,8 @@ static struct ctl_table xr_tunables_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_slot_table_size,
-		.extra2		= &max_slot_table_size
+		.min		= RPCRDMA_MIN_SLOT_TABLE,
+		.max		= RPCRDMA_MAX_SLOT_TABLE
 	},
 	{
 		.procname	= "rdma_max_inline_read",
@@ -100,8 +93,8 @@ static struct ctl_table xr_tunables_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_inline_size,
-		.extra2		= &max_inline_size,
+		.min		= RPCRDMA_MIN_INLINE,
+		.max		= RPCRDMA_MAX_INLINE,
 	},
 	{
 		.procname	= "rdma_max_inline_write",
@@ -109,8 +102,8 @@ static struct ctl_table xr_tunables_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_inline_size,
-		.extra2		= &max_inline_size,
+		.min		= RPCRDMA_MIN_INLINE,
+		.max		= RPCRDMA_MAX_INLINE,
 	},
 	{
 		.procname	= "rdma_inline_write_padding",
@@ -118,8 +111,8 @@ static struct ctl_table xr_tunables_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &max_padding,
+		.min		= 0,
+		.max		= PAGE_SIZE,
 	},
 	{
 		.procname	= "rdma_memreg_strategy",
@@ -127,8 +120,8 @@ static struct ctl_table xr_tunables_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_memreg,
-		.extra2		= &max_memreg,
+		.min		= RPCRDMA_BOUNCEBUFFERS,
+		.max		= RPCRDMA_LAST - 1,
 	},
 	{
 		.procname	= "rdma_pad_optimize",
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 59e36f206ccf..a15f54bf2b3c 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -88,12 +88,6 @@ static unsigned int xs_tcp_fin_timeout __read_mostly = XS_TCP_LINGER_TO;
  * someone else's file names!
  */
 
-static unsigned int min_slot_table_size = RPC_MIN_SLOT_TABLE;
-static unsigned int max_slot_table_size = RPC_MAX_SLOT_TABLE;
-static unsigned int max_tcp_slot_table_limit = RPC_MAX_SLOT_TABLE_LIMIT;
-static unsigned int xprt_min_resvport_limit = RPC_MIN_RESVPORT;
-static unsigned int xprt_max_resvport_limit = RPC_MAX_RESVPORT;
-
 static struct ctl_table_header *sunrpc_table_header;
 
 static struct xprt_class xs_local_transport;
@@ -113,8 +107,8 @@ static struct ctl_table xs_tunables_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_slot_table_size,
-		.extra2		= &max_slot_table_size
+		.min		= RPC_MIN_SLOT_TABLE,
+		.max		= RPC_MAX_SLOT_TABLE
 	},
 	{
 		.procname	= "tcp_slot_table_entries",
@@ -122,8 +116,8 @@ static struct ctl_table xs_tunables_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_slot_table_size,
-		.extra2		= &max_slot_table_size
+		.min		= RPC_MIN_SLOT_TABLE,
+		.max		= RPC_MAX_SLOT_TABLE
 	},
 	{
 		.procname	= "tcp_max_slot_table_entries",
@@ -131,8 +125,8 @@ static struct ctl_table xs_tunables_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_slot_table_size,
-		.extra2		= &max_tcp_slot_table_limit
+		.min		= RPC_MIN_SLOT_TABLE,
+		.max		= RPC_MAX_SLOT_TABLE_LIMIT
 	},
 	{
 		.procname	= "min_resvport",
@@ -140,8 +134,8 @@ static struct ctl_table xs_tunables_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xprt_min_resvport_limit,
-		.extra2		= &xprt_max_resvport_limit
+		.min		= RPC_MIN_RESVPORT,
+		.max		= RPC_MAX_RESVPORT
 	},
 	{
 		.procname	= "max_resvport",
@@ -149,8 +143,8 @@ static struct ctl_table xs_tunables_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &xprt_min_resvport_limit,
-		.extra2		= &xprt_max_resvport_limit
+		.min		= RPC_MIN_RESVPORT,
+		.max		= RPC_MAX_RESVPORT
 	},
 	{
 		.procname	= "tcp_fin_timeout",
-- 
2.25.1


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

* [RFC PATCH 17/39] sysctl: store remaining network sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (15 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 16/39] sysctl: store RPC " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 18/39] sysctl: store security " wen.yang
                   ` (21 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Replace remaining network bound pointers with numeric limits.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/mptcp/ctrl.c         | 32 ++++++++++++-------------
 net/rds/ib_sysctl.c      | 24 ++++++++-----------
 net/rxrpc/sysctl.c       | 51 ++++++++++++++++------------------------
 net/smc/smc_sysctl.c     | 42 +++++++++++++--------------------
 net/tipc/sysctl.c        | 17 ++++++++------
 net/vmw_vsock/af_vsock.c |  4 ++--
 net/x25/sysctl_net_x25.c | 23 ++++++++----------
 7 files changed, 84 insertions(+), 109 deletions(-)

diff --git a/net/mptcp/ctrl.c b/net/mptcp/ctrl.c
index e5a5995c5753..eb826a9b75da 100644
--- a/net/mptcp/ctrl.c
+++ b/net/mptcp/ctrl.c
@@ -20,7 +20,6 @@
 static int mptcp_pernet_id;
 
 #ifdef CONFIG_SYSCTL
-static int mptcp_pm_type_max = __MPTCP_PM_TYPE_MAX;
 #endif
 
 struct mptcp_pernet {
@@ -288,8 +287,8 @@ static struct ctl_table mptcp_sysctl_table[] = {
 		 * value, same as other sysctl or the 'net' tree.
 		 */
 		.proc_handler = proc_dou8vec_minmax,
-		.extra1       = SYSCTL_ZERO,
-		.extra2       = SYSCTL_ONE
+		.min          = 0,
+		.max          = 1
 	},
 	{
 		.procname = "add_addr_timeout",
@@ -302,32 +301,32 @@ static struct ctl_table mptcp_sysctl_table[] = {
 		.maxlen = sizeof(u8),
 		.mode = 0644,
 		.proc_handler = proc_dou8vec_minmax,
-		.extra1       = SYSCTL_ZERO,
-		.extra2       = SYSCTL_ONE
+		.min          = 0,
+		.max          = 1
 	},
 	{
 		.procname = "allow_join_initial_addr_port",
 		.maxlen = sizeof(u8),
 		.mode = 0644,
 		.proc_handler = proc_dou8vec_minmax,
-		.extra1       = SYSCTL_ZERO,
-		.extra2       = SYSCTL_ONE
+		.min          = 0,
+		.max          = 1
 	},
 	{
 		.procname = "stale_loss_cnt",
 		.maxlen = sizeof(unsigned int),
 		.mode = 0644,
 		.proc_handler = proc_douintvec_minmax,
-		.min = 0,
-		.max = UINT_MAX,
+		.min          = 0,
+		.max          = UINT_MAX,
 	},
 	{
 		.procname = "pm_type",
 		.maxlen = sizeof(u8),
 		.mode = 0644,
 		.proc_handler = proc_pm_type,
-		.extra1       = SYSCTL_ZERO,
-		.extra2       = &mptcp_pm_type_max
+		.min          = 0,
+		.max          = __MPTCP_PM_TYPE_MAX
 	},
 	{
 		.procname = "scheduler",
@@ -352,15 +351,16 @@ static struct ctl_table mptcp_sysctl_table[] = {
 		.maxlen = sizeof(unsigned int),
 		.mode = 0644,
 		.proc_handler = proc_blackhole_detect_timeout,
-		.extra1 = SYSCTL_ZERO,
+		.min          = 0,
+		.max          = INT_MAX,
 	},
 	{
 		.procname = "syn_retrans_before_tcp_fallback",
 		.maxlen = sizeof(u8),
 		.mode = 0644,
 		.proc_handler = proc_dou8vec_minmax,
-		.min = 0,
-		.max = U8_MAX,
+		.min          = 0,
+		.max          = U8_MAX,
 	},
 	{
 		.procname = "path_manager",
@@ -379,8 +379,8 @@ static struct ctl_table mptcp_sysctl_table[] = {
 		.maxlen = sizeof(u8),
 		.mode = 0644,
 		.proc_handler = proc_dou8vec_minmax,
-		.extra1       = SYSCTL_ZERO,
-		.extra2       = SYSCTL_ONE
+		.min          = 0,
+		.max          = 1
 	},
 };
 
diff --git a/net/rds/ib_sysctl.c b/net/rds/ib_sysctl.c
index b7757f31001c..3b81e686123a 100644
--- a/net/rds/ib_sysctl.c
+++ b/net/rds/ib_sysctl.c
@@ -42,13 +42,7 @@ static struct ctl_table_header *rds_ib_sysctl_hdr;
 unsigned long rds_ib_sysctl_max_send_wr = RDS_IB_DEFAULT_SEND_WR;
 unsigned long rds_ib_sysctl_max_recv_wr = RDS_IB_DEFAULT_RECV_WR;
 unsigned long rds_ib_sysctl_max_recv_allocation = (128 * 1024 * 1024) / RDS_FRAG_SIZE;
-static unsigned long rds_ib_sysctl_max_wr_min = 1;
-/* hardware will fail CQ creation long before this */
-static unsigned long rds_ib_sysctl_max_wr_max = (u32)~0;
-
 unsigned long rds_ib_sysctl_max_unsig_wrs = 16;
-static unsigned long rds_ib_sysctl_max_unsig_wr_min = 1;
-static unsigned long rds_ib_sysctl_max_unsig_wr_max = 64;
 
 /*
  * This sysctl does nothing.
@@ -69,8 +63,9 @@ static struct ctl_table rds_ib_sysctl_table[] = {
 		.maxlen         = sizeof(unsigned long),
 		.mode           = 0644,
 		.proc_handler   = proc_doulongvec_minmax,
-		.extra1		= &rds_ib_sysctl_max_wr_min,
-		.extra2		= &rds_ib_sysctl_max_wr_max,
+		.min		= 1,
+		/* Hardware will fail CQ creation long before this. */
+		.max		= (u32)~0,
 	},
 	{
 		.procname       = "max_recv_wr",
@@ -78,8 +73,9 @@ static struct ctl_table rds_ib_sysctl_table[] = {
 		.maxlen         = sizeof(unsigned long),
 		.mode           = 0644,
 		.proc_handler   = proc_doulongvec_minmax,
-		.extra1		= &rds_ib_sysctl_max_wr_min,
-		.extra2		= &rds_ib_sysctl_max_wr_max,
+		.min		= 1,
+		/* Hardware will fail CQ creation long before this. */
+		.max		= (u32)~0,
 	},
 	{
 		.procname       = "max_unsignaled_wr",
@@ -87,8 +83,8 @@ static struct ctl_table rds_ib_sysctl_table[] = {
 		.maxlen         = sizeof(unsigned long),
 		.mode           = 0644,
 		.proc_handler   = proc_doulongvec_minmax,
-		.extra1		= &rds_ib_sysctl_max_unsig_wr_min,
-		.extra2		= &rds_ib_sysctl_max_unsig_wr_max,
+		.min		= 1,
+		.max		= 64,
 	},
 	{
 		.procname       = "max_recv_allocation",
@@ -96,8 +92,8 @@ static struct ctl_table rds_ib_sysctl_table[] = {
 		.maxlen         = sizeof(unsigned long),
 		.mode           = 0644,
 		.proc_handler   = proc_doulongvec_minmax,
-		.min           = 0,
-		.max           = ULONG_MAX,
+		.min            = 0,
+		.max            = ULONG_MAX,
 	},
 	{
 		.procname	= "flow_control",
diff --git a/net/rxrpc/sysctl.c b/net/rxrpc/sysctl.c
index 46a20cf4c402..ceac11eb1d97 100644
--- a/net/rxrpc/sysctl.c
+++ b/net/rxrpc/sysctl.c
@@ -11,18 +11,7 @@
 #include "ar-internal.h"
 
 static struct ctl_table_header *rxrpc_sysctl_reg_table;
-static const unsigned int rxrpc_rx_mtu_min = 500;
-static const unsigned int rxrpc_jumbo_max = RXRPC_MAX_NR_JUMBO;
-static const unsigned int four = 4;
-static const unsigned int max_backlog = RXRPC_BACKLOG_MAX - 1;
-static const unsigned int n_65535 = 65535;
-static const unsigned int n_max_acks = 255;
-static const unsigned long one_ms = 1;
-static const unsigned long max_ms = 1000;
-static const unsigned long one_jiffy = 1;
-static const unsigned long max_jiffies = MAX_JIFFY_OFFSET;
 #ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY
-static const unsigned long max_500 = 500;
 #endif
 
 /*
@@ -39,8 +28,8 @@ static struct ctl_table rxrpc_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra1		= (void *)&one_ms,
-		.extra2		= (void *)&max_ms,
+		.min		= 1,
+		.max		= 1000,
 	},
 	{
 		.procname	= "idle_ack_delay",
@@ -48,8 +37,8 @@ static struct ctl_table rxrpc_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra1		= (void *)&one_ms,
-		.extra2		= (void *)&max_ms,
+		.min		= 1,
+		.max		= 1000,
 	},
 	{
 		.procname	= "idle_conn_expiry",
@@ -57,8 +46,8 @@ static struct ctl_table rxrpc_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_ms_jiffies_minmax,
-		.extra1		= (void *)&one_jiffy,
-		.extra2		= (void *)&max_jiffies,
+		.min		= 1,
+		.max		= MAX_JIFFY_OFFSET,
 	},
 	{
 		.procname	= "idle_conn_fast_expiry",
@@ -66,8 +55,8 @@ static struct ctl_table rxrpc_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_ms_jiffies_minmax,
-		.extra1		= (void *)&one_jiffy,
-		.extra2		= (void *)&max_jiffies,
+		.min		= 1,
+		.max		= MAX_JIFFY_OFFSET,
 	},
 
 	/* Values used in milliseconds */
@@ -78,8 +67,8 @@ static struct ctl_table rxrpc_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= proc_doulongvec_minmax,
-		.extra1		= (void *)SYSCTL_LONG_ZERO,
-		.extra2		= (void *)&max_500,
+		.min		= 0,
+		.max		= 500,
 	},
 #endif
 
@@ -90,8 +79,8 @@ static struct ctl_table rxrpc_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= (void *)SYSCTL_ONE,
-		.extra2		= (void *)&n_65535,
+		.min		= 1,
+		.max		= 65535,
 	},
 	{
 		.procname	= "max_backlog",
@@ -99,8 +88,8 @@ static struct ctl_table rxrpc_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= (void *)&four,
-		.extra2		= (void *)&max_backlog,
+		.min		= 4,
+		.max		= RXRPC_BACKLOG_MAX - 1,
 	},
 	{
 		.procname	= "rx_window_size",
@@ -108,8 +97,8 @@ static struct ctl_table rxrpc_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= (void *)SYSCTL_ONE,
-		.extra2		= (void *)&n_max_acks,
+		.min		= 1,
+		.max		= 255,
 	},
 	{
 		.procname	= "rx_mtu",
@@ -117,8 +106,8 @@ static struct ctl_table rxrpc_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= (void *)&rxrpc_rx_mtu_min,
-		.extra2		= (void *)&n_65535,
+		.min		= 500,
+		.max		= 65535,
 	},
 	{
 		.procname	= "rx_jumbo_max",
@@ -126,8 +115,8 @@ static struct ctl_table rxrpc_sysctl_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= (void *)SYSCTL_ONE,
-		.extra2		= (void *)&rxrpc_jumbo_max,
+		.min		= 1,
+		.max		= RXRPC_MAX_NR_JUMBO,
 	},
 };
 
diff --git a/net/smc/smc_sysctl.c b/net/smc/smc_sysctl.c
index 09dad48337f6..2baa974496c4 100644
--- a/net/smc/smc_sysctl.c
+++ b/net/smc/smc_sysctl.c
@@ -21,18 +21,8 @@
 #include "smc_sysctl.h"
 #include "smc_hs_bpf.h"
 
-static int min_sndbuf = SMC_BUF_MIN_SIZE;
-static int min_rcvbuf = SMC_BUF_MIN_SIZE;
-static int max_sndbuf = INT_MAX / 2;
-static int max_rcvbuf = INT_MAX / 2;
 static const int net_smc_wmem_init = (64 * 1024);
 static const int net_smc_rmem_init = (64 * 1024);
-static int links_per_lgr_min = SMC_LINKS_ADD_LNK_MIN;
-static int links_per_lgr_max = SMC_LINKS_ADD_LNK_MAX;
-static int conns_per_lgr_min = SMC_CONN_PER_LGR_MIN;
-static int conns_per_lgr_max = SMC_CONN_PER_LGR_MAX;
-static unsigned int smcr_max_wr_min = 2;
-static unsigned int smcr_max_wr_max = 2048;
 
 #if IS_ENABLED(CONFIG_SMC_HS_CTRL_BPF)
 static int smc_net_replace_smc_hs_ctrl(struct net *net, const char *name)
@@ -111,8 +101,8 @@ static const struct ctl_table smc_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname	= "smcr_testlink_time",
@@ -127,8 +117,8 @@ static const struct ctl_table smc_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_sndbuf,
-		.extra2		= &max_sndbuf,
+		.min		= SMC_BUF_MIN_SIZE,
+		.max		= INT_MAX / 2,
 	},
 	{
 		.procname	= "rmem",
@@ -136,8 +126,8 @@ static const struct ctl_table smc_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_rcvbuf,
-		.extra2		= &max_rcvbuf,
+		.min		= SMC_BUF_MIN_SIZE,
+		.max		= INT_MAX / 2,
 	},
 	{
 		.procname	= "smcr_max_links_per_lgr",
@@ -145,8 +135,8 @@ static const struct ctl_table smc_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &links_per_lgr_min,
-		.extra2		= &links_per_lgr_max,
+		.min		= SMC_LINKS_ADD_LNK_MIN,
+		.max		= SMC_LINKS_ADD_LNK_MAX,
 	},
 	{
 		.procname	= "smcr_max_conns_per_lgr",
@@ -154,8 +144,8 @@ static const struct ctl_table smc_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &conns_per_lgr_min,
-		.extra2		= &conns_per_lgr_max,
+		.min		= SMC_CONN_PER_LGR_MIN,
+		.max		= SMC_CONN_PER_LGR_MAX,
 	},
 	{
 		.procname	= "limit_smc_hs",
@@ -163,8 +153,8 @@ static const struct ctl_table smc_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "smcr_max_send_wr",
@@ -172,8 +162,8 @@ static const struct ctl_table smc_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &smcr_max_wr_min,
-		.extra2		= &smcr_max_wr_max,
+		.min		= 2,
+		.max		= 2048,
 	},
 	{
 		.procname	= "smcr_max_recv_wr",
@@ -181,8 +171,8 @@ static const struct ctl_table smc_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &smcr_max_wr_min,
-		.extra2		= &smcr_max_wr_max,
+		.min		= 2,
+		.max		= 2048,
 	},
 #if IS_ENABLED(CONFIG_SMC_HS_CTRL_BPF)
 	{
diff --git a/net/tipc/sysctl.c b/net/tipc/sysctl.c
index 8352c790c8fe..446fb7d188bb 100644
--- a/net/tipc/sysctl.c
+++ b/net/tipc/sysctl.c
@@ -49,7 +49,8 @@ static struct ctl_table tipc_table[] = {
 		.maxlen		= sizeof(sysctl_tipc_rmem),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "named_timeout",
@@ -57,7 +58,8 @@ static struct ctl_table tipc_table[] = {
 		.maxlen		= sizeof(sysctl_tipc_named_timeout),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname       = "sk_filter",
@@ -65,8 +67,8 @@ static struct ctl_table tipc_table[] = {
 		.maxlen         = sizeof(sysctl_tipc_sk_filter),
 		.mode           = 0644,
 		.proc_handler   = proc_doulongvec_minmax,
-		.min           = 0,
-		.max           = ULONG_MAX,
+		.min            = 0,
+		.max            = ULONG_MAX,
 	},
 #ifdef CONFIG_TIPC_CRYPTO
 	{
@@ -75,7 +77,8 @@ static struct ctl_table tipc_table[] = {
 		.maxlen		= sizeof(sysctl_tipc_max_tfms),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "key_exchange_enabled",
@@ -83,8 +86,8 @@ static struct ctl_table tipc_table[] = {
 		.maxlen		= sizeof(sysctl_tipc_key_exchange_enabled),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #endif
 	{
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index d0508887a2fb..f7c90c82c490 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -3038,8 +3038,8 @@ static const struct ctl_table vsock_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
diff --git a/net/x25/sysctl_net_x25.c b/net/x25/sysctl_net_x25.c
index 643f50874dfe..e5399a66d8d7 100644
--- a/net/x25/sysctl_net_x25.c
+++ b/net/x25/sysctl_net_x25.c
@@ -13,9 +13,6 @@
 #include <linux/init.h>
 #include <net/x25.h>
 
-static int min_timer[] = {   1 * HZ };
-static int max_timer[] = { 300 * HZ };
-
 static struct ctl_table_header *x25_table_header;
 
 static struct ctl_table x25_table[] = {
@@ -25,8 +22,8 @@ static struct ctl_table x25_table[] = {
 		.maxlen =	sizeof(int),
 		.mode =		0644,
 		.proc_handler =	proc_dointvec_minmax,
-		.extra1 =	&min_timer,
-		.extra2 =	&max_timer,
+		.min          =	HZ,
+		.max          =	300 * HZ,
 	},
 	{
 		.procname =	"call_request_timeout",
@@ -34,8 +31,8 @@ static struct ctl_table x25_table[] = {
 		.maxlen =	sizeof(int),
 		.mode =		0644,
 		.proc_handler =	proc_dointvec_minmax,
-		.extra1 =	&min_timer,
-		.extra2 =	&max_timer,
+		.min          =	HZ,
+		.max          =	300 * HZ,
 	},
 	{
 		.procname =	"reset_request_timeout",
@@ -43,8 +40,8 @@ static struct ctl_table x25_table[] = {
 		.maxlen =	sizeof(int),
 		.mode =		0644,
 		.proc_handler =	proc_dointvec_minmax,
-		.extra1 =	&min_timer,
-		.extra2 =	&max_timer,
+		.min          =	HZ,
+		.max          =	300 * HZ,
 	},
 	{
 		.procname =	"clear_request_timeout",
@@ -52,8 +49,8 @@ static struct ctl_table x25_table[] = {
 		.maxlen =	sizeof(int),
 		.mode =		0644,
 		.proc_handler =	proc_dointvec_minmax,
-		.extra1 =	&min_timer,
-		.extra2 =	&max_timer,
+		.min          =	HZ,
+		.max          =	300 * HZ,
 	},
 	{
 		.procname =	"acknowledgement_hold_back_timeout",
@@ -61,8 +58,8 @@ static struct ctl_table x25_table[] = {
 		.maxlen =	sizeof(int),
 		.mode =		0644,
 		.proc_handler =	proc_dointvec_minmax,
-		.extra1 =	&min_timer,
-		.extra2 =	&max_timer,
+		.min          =	HZ,
+		.max          =	300 * HZ,
 	},
 	{
 		.procname =	"x25_forward",
-- 
2.25.1


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

* [RFC PATCH 18/39] sysctl: store security sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (16 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 17/39] sysctl: store remaining network " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 19/39] io_uring: store its " wen.yang
                   ` (20 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Replace security bound pointers with numeric limits.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 security/keys/sysctl.c     | 25 +++++++++++++------------
 security/loadpin/loadpin.c |  4 ++--
 2 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c
index cde08c478f32..f0017dbff963 100644
--- a/security/keys/sysctl.c
+++ b/security/keys/sysctl.c
@@ -6,6 +6,7 @@
  */
 
 #include <linux/key.h>
+#include <linux/limits.h>
 #include <linux/sysctl.h>
 #include "internal.h"
 
@@ -16,8 +17,8 @@ static const struct ctl_table key_sysctls[] = {
 		.maxlen = sizeof(unsigned),
 		.mode = 0644,
 		.proc_handler = proc_dointvec_minmax,
-		.extra1 = (void *) SYSCTL_ONE,
-		.extra2 = (void *) SYSCTL_INT_MAX,
+		.min          = 1,
+		.max          = INT_MAX,
 	},
 	{
 		.procname = "maxbytes",
@@ -25,8 +26,8 @@ static const struct ctl_table key_sysctls[] = {
 		.maxlen = sizeof(unsigned),
 		.mode = 0644,
 		.proc_handler = proc_dointvec_minmax,
-		.extra1 = (void *) SYSCTL_ONE,
-		.extra2 = (void *) SYSCTL_INT_MAX,
+		.min          = 1,
+		.max          = INT_MAX,
 	},
 	{
 		.procname = "root_maxkeys",
@@ -34,8 +35,8 @@ static const struct ctl_table key_sysctls[] = {
 		.maxlen = sizeof(unsigned),
 		.mode = 0644,
 		.proc_handler = proc_dointvec_minmax,
-		.extra1 = (void *) SYSCTL_ONE,
-		.extra2 = (void *) SYSCTL_INT_MAX,
+		.min          = 1,
+		.max          = INT_MAX,
 	},
 	{
 		.procname = "root_maxbytes",
@@ -43,8 +44,8 @@ static const struct ctl_table key_sysctls[] = {
 		.maxlen = sizeof(unsigned),
 		.mode = 0644,
 		.proc_handler = proc_dointvec_minmax,
-		.extra1 = (void *) SYSCTL_ONE,
-		.extra2 = (void *) SYSCTL_INT_MAX,
+		.min          = 1,
+		.max          = INT_MAX,
 	},
 	{
 		.procname = "gc_delay",
@@ -52,8 +53,8 @@ static const struct ctl_table key_sysctls[] = {
 		.maxlen = sizeof(unsigned),
 		.mode = 0644,
 		.proc_handler = proc_dointvec_minmax,
-		.extra1 = (void *) SYSCTL_ZERO,
-		.extra2 = (void *) SYSCTL_INT_MAX,
+		.min          = 0,
+		.max          = INT_MAX,
 	},
 #ifdef CONFIG_PERSISTENT_KEYRINGS
 	{
@@ -62,8 +63,8 @@ static const struct ctl_table key_sysctls[] = {
 		.maxlen = sizeof(unsigned),
 		.mode = 0644,
 		.proc_handler = proc_dointvec_minmax,
-		.extra1 = (void *) SYSCTL_ZERO,
-		.extra2 = (void *) SYSCTL_INT_MAX,
+		.min          = 0,
+		.max          = INT_MAX,
 	},
 #endif
 };
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index f71861f98e1a..7993ab17eacf 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -72,8 +72,8 @@ static const struct ctl_table loadpin_sysctl_table[] = {
 		.maxlen         = sizeof(int),
 		.mode           = 0644,
 		.proc_handler   = proc_handler_loadpin,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min            = 0,
+		.max            = 1,
 	},
 };
 #endif
-- 
2.25.1


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

* [RFC PATCH 19/39] io_uring: store its sysctl bounds by value
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (17 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 18/39] sysctl: store security " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:34 ` [RFC PATCH 20/39] sysctl: refresh dynamic scheduler and PID bounds per call wen.yang
                   ` (19 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Replace io_uring bound pointers with numeric limits.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 io_uring/io_uring.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 8ffe0c9a4e29..48e03c3c066c 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -138,8 +138,8 @@ static const struct ctl_table kernel_io_uring_disabled_table[] = {
 		.maxlen		= sizeof(sysctl_io_uring_disabled),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname	= "io_uring_group",
-- 
2.25.1


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

* [RFC PATCH 20/39] sysctl: refresh dynamic scheduler and PID bounds per call
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (18 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 19/39] io_uring: store its " wen.yang
@ 2026-10-28 16:34 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 21/39] sysctl: refresh device-dependent " wen.yang
                   ` (18 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:34 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Read current scheduler and PID limits into local tables before validation.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 kernel/fork.c           |  8 ++++----
 kernel/pid.c            | 16 +++++++++++++---
 kernel/pid_namespace.c  |  6 +++---
 kernel/pid_sysctl.h     |  6 +++---
 kernel/sched/deadline.c | 29 +++++++++++++++++++++++++----
 kernel/sched/rt.c       | 13 ++++++++-----
 6 files changed, 56 insertions(+), 22 deletions(-)

diff --git a/kernel/fork.c b/kernel/fork.c
index 18b6dd851642..44947ef38cb4 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -3364,13 +3364,11 @@ static int sysctl_max_threads(const struct ctl_table *table, int write,
 	struct ctl_table t;
 	int ret;
 	int threads = max_threads;
-	int min = 1;
-	int max = MAX_THREADS;
 
 	t = *table;
 	t.data = &threads;
-	t.extra1 = &min;
-	t.extra2 = &max;
+	t.min = 1;
+	t.max = MAX_THREADS;
 
 	ret = proc_dointvec_minmax(&t, write, buffer, lenp, ppos);
 	if (ret || !write)
@@ -3388,6 +3386,8 @@ static const struct ctl_table fork_sysctl_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= sysctl_max_threads,
+		.min		= 1,
+		.max		= MAX_THREADS,
 	},
 };
 
diff --git a/kernel/pid.c b/kernel/pid.c
index adf721606768..21ffdc3d4ea7 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -766,15 +766,25 @@ static struct ctl_table_root pid_table_root = {
 	.set_ownership	= pid_table_root_set_ownership,
 };
 
+static int pid_max_handler(const struct ctl_table *table, int write,
+			   void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.min = (unsigned long)READ_ONCE(pid_max_min);
+	tmp.max = (unsigned long)READ_ONCE(pid_max_max);
+	return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static const struct ctl_table pid_table[] = {
 	{
 		.procname	= "pid_max",
 		.data		= &init_pid_ns.pid_max,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &pid_max_min,
-		.extra2		= &pid_max_max,
+		.proc_handler	= pid_max_handler,
+		.min		= (unsigned long)INT_MIN,
+		.max		= INT_MAX,
 	},
 };
 #endif
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index 127a5fd23e70..fe173ec63cb2 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -297,7 +297,7 @@ static int pid_ns_ctl_handler(const struct ctl_table *table, int write,
 	next = idr_get_cursor(&pid_ns->idr) - 1;
 
 	tmp.data = &next;
-	tmp.extra2 = &pid_ns->pid_max;
+	tmp.max = READ_ONCE(pid_ns->pid_max);
 	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 	if (!ret && write)
 		idr_set_cursor(&pid_ns->idr, next + 1);
@@ -311,8 +311,8 @@ static const struct ctl_table pid_ns_ctl_table[] = {
 		.maxlen = sizeof(int),
 		.mode = 0666, /* permissions are checked in the handler */
 		.proc_handler = pid_ns_ctl_handler,
-		.extra1 = SYSCTL_ZERO,
-		.extra2 = &init_pid_ns.pid_max,
+		.min = 0,
+		.max = INT_MAX,
 	},
 };
 #endif	/* CONFIG_CHECKPOINT_RESTORE */
diff --git a/kernel/pid_sysctl.h b/kernel/pid_sysctl.h
index 5d8f981de7c5..a7b90380285d 100644
--- a/kernel/pid_sysctl.h
+++ b/kernel/pid_sysctl.h
@@ -23,7 +23,7 @@ static int pid_mfd_noexec_dointvec_minmax(const struct ctl_table *table,
 	scope = max(READ_ONCE(ns->memfd_noexec_scope), parent_scope);
 
 	table_copy.data = &scope;
-	table_copy.extra1 = &parent_scope;
+	table_copy.min = (unsigned long)parent_scope;
 
 	err = proc_dointvec_minmax(&table_copy, write, buf, lenp, ppos);
 	if (!err && write)
@@ -38,8 +38,8 @@ static const struct ctl_table pid_ns_ctl_table_vm[] = {
 		.maxlen		= sizeof(init_pid_ns.memfd_noexec_scope),
 		.mode		= 0644,
 		.proc_handler	= pid_mfd_noexec_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 };
 static inline void register_pid_ns_sysctl_table_vm(void)
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index c0ebdcde5fe5..bf27abdd5c33 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -19,6 +19,7 @@
 #include <linux/cpuset.h>
 #include <linux/sched/clock.h>
 #include <linux/sched/deadline.h>
+#include <linux/sched/isolation.h>
 #include <uapi/linux/sched/types.h>
 #include "sched.h"
 #include "pelt.h"
@@ -31,22 +32,42 @@
 static unsigned int sysctl_sched_dl_period_max = 1 << 22; /* ~4 seconds */
 static unsigned int sysctl_sched_dl_period_min = 100;     /* 100 us */
 #ifdef CONFIG_SYSCTL
+static int sched_dl_period_max_handler(const struct ctl_table *table, int write,
+				       void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.min = READ_ONCE(sysctl_sched_dl_period_min);
+	return proc_douintvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
+static int sched_dl_period_min_handler(const struct ctl_table *table, int write,
+				       void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.max = READ_ONCE(sysctl_sched_dl_period_max);
+	return proc_douintvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static const struct ctl_table sched_dl_sysctls[] = {
 	{
 		.procname       = "sched_deadline_period_max_us",
 		.data           = &sysctl_sched_dl_period_max,
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
-		.proc_handler   = proc_douintvec_minmax,
-		.extra1         = (void *)&sysctl_sched_dl_period_min,
+		.proc_handler   = sched_dl_period_max_handler,
+		.min		= 0,
+		.max		= UINT_MAX,
 	},
 	{
 		.procname       = "sched_deadline_period_min_us",
 		.data           = &sysctl_sched_dl_period_min,
 		.maxlen         = sizeof(unsigned int),
 		.mode           = 0644,
-		.proc_handler   = proc_douintvec_minmax,
-		.extra2         = (void *)&sysctl_sched_dl_period_max,
+		.proc_handler   = sched_dl_period_min_handler,
+		.min		= 0,
+		.max		= UINT_MAX,
 	},
 };
 
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 1535046a23ff..26e9b2ac8838 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -36,8 +36,8 @@ static const struct ctl_table sched_rt_sysctls[] = {
 		.maxlen         = sizeof(int),
 		.mode           = 0644,
 		.proc_handler   = sched_rt_handler,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = SYSCTL_INT_MAX,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname       = "sched_rt_runtime_us",
@@ -45,8 +45,8 @@ static const struct ctl_table sched_rt_sysctls[] = {
 		.maxlen         = sizeof(int),
 		.mode           = 0644,
 		.proc_handler   = sched_rt_handler,
-		.extra1         = SYSCTL_NEG_ONE,
-		.extra2         = (void *)&sysctl_sched_rt_period,
+		.min		= (unsigned long)-1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname       = "sched_rr_timeslice_ms",
@@ -2872,6 +2872,7 @@ static int sched_rt_global_validate(void)
 static int sched_rt_handler(const struct ctl_table *table, int write, void *buffer,
 		size_t *lenp, loff_t *ppos)
 {
+	struct ctl_table tmp = *table;
 	int old_period, old_runtime;
 	static DEFINE_MUTEX(mutex);
 	int ret;
@@ -2881,7 +2882,9 @@ static int sched_rt_handler(const struct ctl_table *table, int write, void *buff
 	old_period = sysctl_sched_rt_period;
 	old_runtime = sysctl_sched_rt_runtime;
 
-	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+	if (table->data == &sysctl_sched_rt_runtime)
+		tmp.max = READ_ONCE(sysctl_sched_rt_period);
+	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 
 	if (!ret && write) {
 		ret = sched_rt_global_validate();
-- 
2.25.1


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

* [RFC PATCH 21/39] sysctl: refresh device-dependent bounds per call
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (19 preceding siblings ...)
  2026-10-28 16:34 ` [RFC PATCH 20/39] sysctl: refresh dynamic scheduler and PID bounds per call wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 22/39] sysctl: refresh namespace IPC " wen.yang
                   ` (17 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Refresh device-dependent limits before validating sysctl writes.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 drivers/base/power/main.c        | 21 +++++++++++++++------
 drivers/gpu/drm/i915/i915_perf.c | 19 ++++++++++++++-----
 2 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 88a31e09e4f3..8346875a4fa2 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -543,7 +543,6 @@ MODULE_PARM_DESC(dpm_watchdog_enabled, "Enable DPM watchdog");
 static unsigned int __read_mostly dpm_watchdog_timeout = CONFIG_DPM_WATCHDOG_TIMEOUT;
 static unsigned int __read_mostly dpm_watchdog_warning_timeout =
 						CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT;
-static const unsigned int dpm_watchdog_timeout_max = CONFIG_DPM_WATCHDOG_TIMEOUT;
 
 static int proc_dodpm_watchdog_timeout_secs(const struct ctl_table *table,
 					    int write, void *buffer,
@@ -565,23 +564,33 @@ static int proc_dodpm_watchdog_timeout_secs(const struct ctl_table *table,
 	return 0;
 }
 
+static int proc_dodpm_watchdog_warning_timeout_secs(const struct ctl_table *table,
+						    int write, void *buffer, size_t *lenp,
+						    loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.max = READ_ONCE(dpm_watchdog_timeout);
+	return proc_douintvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static const struct ctl_table dpm_watchdog_sysctls[] = {
 	{
 		.procname	= "dpm_watchdog_timeout_secs",
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dodpm_watchdog_timeout_secs,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= (void *)&dpm_watchdog_timeout_max,
+		.min		= 1,
+		.max		= CONFIG_DPM_WATCHDOG_TIMEOUT,
 	},
 	{
 		.procname	= "dpm_watchdog_warning_timeout_secs",
 		.data		= &dpm_watchdog_warning_timeout,
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
-		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= (void *)&dpm_watchdog_timeout,
+		.proc_handler	= proc_dodpm_watchdog_warning_timeout_secs,
+		.min		= 1,
+		.max		= UINT_MAX,
 	},
 };
 
diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
index 19b82427aa41..2e758f2008db 100644
--- a/drivers/gpu/drm/i915/i915_perf.c
+++ b/drivers/gpu/drm/i915/i915_perf.c
@@ -4795,6 +4795,15 @@ int i915_perf_remove_config_ioctl(struct drm_device *dev, void *data,
 	return ret;
 }
 
+static int oa_max_sample_rate_handler(const struct ctl_table *table, int write,
+				      void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.max = (unsigned long)READ_ONCE(oa_sample_rate_hard_limit);
+	return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static const struct ctl_table oa_table[] = {
 	{
 	 .procname = "perf_stream_paranoid",
@@ -4802,17 +4811,17 @@ static const struct ctl_table oa_table[] = {
 	 .maxlen = sizeof(i915_perf_stream_paranoid),
 	 .mode = 0644,
 	 .proc_handler = proc_dointvec_minmax,
-	 .extra1 = SYSCTL_ZERO,
-	 .extra2 = SYSCTL_ONE,
+	 .min = 0,
+	 .max = 1,
 	 },
 	{
 	 .procname = "oa_max_sample_rate",
 	 .data = &i915_oa_max_sample_rate,
 	 .maxlen = sizeof(i915_oa_max_sample_rate),
 	 .mode = 0644,
-	 .proc_handler = proc_dointvec_minmax,
-	 .extra1 = SYSCTL_ZERO,
-	 .extra2 = &oa_sample_rate_hard_limit,
+	 .proc_handler = oa_max_sample_rate_handler,
+	 .min = 0,
+	 .max = INT_MAX,
 	 },
 };
 
-- 
2.25.1


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

* [RFC PATCH 22/39] sysctl: refresh namespace IPC bounds per call
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (20 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 21/39] sysctl: refresh device-dependent " wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 23/39] yama: preserve scope locking with numeric sysctl bounds wen.yang
                   ` (16 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Refresh IPC limits before validating namespace sysctl writes.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 ipc/ipc_sysctl.c | 50 ++++++++++++++++++++++++++++--------------------
 1 file changed, 29 insertions(+), 21 deletions(-)

diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c
index e02e2ea70907..29a1f442c503 100644
--- a/ipc/ipc_sysctl.c
+++ b/ipc/ipc_sysctl.c
@@ -6,7 +6,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/limits.h>
 #include <linux/ipc.h>
 #include <linux/nsproxy.h>
 #include <linux/sysctl.h>
@@ -74,6 +73,15 @@ int ipc_mni = IPCMNI;
 int ipc_mni_shift = IPCMNI_SHIFT;
 int ipc_min_cycle = RADIX_TREE_MAP_SIZE;
 
+static int proc_ipc_mni(const struct ctl_table *table, int write,
+			void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.max = READ_ONCE(ipc_mni);
+	return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static const struct ctl_table ipc_sysctls[] = {
 	{
 		.procname	= "shmmax",
@@ -98,9 +106,9 @@ static const struct ctl_table ipc_sysctls[] = {
 		.data		= &init_ipc_ns.shm_ctlmni,
 		.maxlen		= sizeof(init_ipc_ns.shm_ctlmni),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &ipc_mni,
+		.proc_handler	= proc_ipc_mni,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "shm_rmid_forced",
@@ -108,8 +116,8 @@ static const struct ctl_table ipc_sysctls[] = {
 		.maxlen		= sizeof(init_ipc_ns.shm_rmid_forced),
 		.mode		= 0644,
 		.proc_handler	= proc_ipc_dointvec_minmax_orphans,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "msgmax",
@@ -117,17 +125,17 @@ static const struct ctl_table ipc_sysctls[] = {
 		.maxlen		= sizeof(init_ipc_ns.msg_ctlmax),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "msgmni",
 		.data		= &init_ipc_ns.msg_ctlmni,
 		.maxlen		= sizeof(init_ipc_ns.msg_ctlmni),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &ipc_mni,
+		.proc_handler	= proc_ipc_mni,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "auto_msgmni",
@@ -135,8 +143,8 @@ static const struct ctl_table ipc_sysctls[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_ipc_auto_msgmni,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	=  "msgmnb",
@@ -144,8 +152,8 @@ static const struct ctl_table ipc_sysctls[] = {
 		.maxlen		= sizeof(init_ipc_ns.msg_ctlmnb),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "sem",
@@ -161,8 +169,8 @@ static const struct ctl_table ipc_sysctls[] = {
 		.maxlen		= sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id),
 		.mode		= 0444,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "msg_next_id",
@@ -170,8 +178,8 @@ static const struct ctl_table ipc_sysctls[] = {
 		.maxlen		= sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id),
 		.mode		= 0444,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "shm_next_id",
@@ -179,8 +187,8 @@ static const struct ctl_table ipc_sysctls[] = {
 		.maxlen		= sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id),
 		.mode		= 0444,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 #endif
 };
-- 
2.25.1


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

* [RFC PATCH 23/39] yama: preserve scope locking with numeric sysctl bounds
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (21 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 22/39] sysctl: refresh namespace IPC " wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 24/39] net: refresh core sysctl limits without bound pointers wen.yang
                   ` (15 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Set the numeric minimum to the maximum when ptrace scope is locked.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 security/yama/yama_lsm.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index cef3776cf3b2..aa4deec29919 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -439,14 +439,12 @@ static int yama_dointvec_minmax(const struct ctl_table *table, int write,
 
 	/* Lock the max value if it ever gets set. */
 	table_copy = *table;
-	if (*(int *)table_copy.data == *(int *)table_copy.extra2)
-		table_copy.extra1 = table_copy.extra2;
+	if (READ_ONCE(*(int *)table_copy.data) == (int)table_copy.max)
+		table_copy.min = table_copy.max;
 
 	return proc_dointvec_minmax(&table_copy, write, buffer, lenp, ppos);
 }
 
-static int max_scope = YAMA_SCOPE_NO_ATTACH;
-
 static const struct ctl_table yama_sysctl_table[] = {
 	{
 		.procname       = "ptrace_scope",
@@ -454,8 +452,8 @@ static const struct ctl_table yama_sysctl_table[] = {
 		.maxlen         = sizeof(int),
 		.mode           = 0644,
 		.proc_handler   = yama_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = &max_scope,
+		.min		= 0,
+		.max		= YAMA_SCOPE_NO_ATTACH,
 	},
 };
 static void __init yama_init_sysctl(void)
-- 
2.25.1


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

* [RFC PATCH 24/39] net: refresh core sysctl limits without bound pointers
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (22 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 23/39] yama: preserve scope locking with numeric sysctl bounds wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 25/39] sctp: resolve numeric ranges from the current namespace wen.yang
                   ` (14 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Refresh core network limits before validating sysctl writes.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/core/sysctl_net_core.c | 118 ++++++++++++++++++++-----------------
 1 file changed, 65 insertions(+), 53 deletions(-)

diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index 473c162736d6..1721e63240c9 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -32,12 +32,6 @@
 #include "dev.h"
 #include "net-sysfs.h"
 
-static int int_3600 = 3600;
-static int min_sndbuf = SOCK_MIN_SNDBUF;
-static int min_rcvbuf = SOCK_MIN_RCVBUF;
-static int max_skb_frags = MAX_SKB_FRAGS;
-static int min_mem_pcpu_rsv = SK_MEMORY_PCPU_RESERVE;
-static int netdev_budget_usecs_min = 2 * USEC_PER_SEC / HZ;
 
 static int net_msg_warn;	/* Unused, but still a sysctl */
 
@@ -146,7 +140,7 @@ static int rps_sock_flow_sysctl(const struct ctl_table *table, int write,
 	struct ctl_table tmp = {
 		.data = &size,
 		.maxlen = sizeof(size),
-		.mode = table->mode
+		.mode = table->mode,
 	};
 	void *tofree = NULL;
 	int ret, i;
@@ -384,8 +378,8 @@ static int proc_dointvec_minmax_bpf_enable(const struct ctl_table *table, int wr
 					   loff_t *ppos)
 {
 	int ret, jit_enable = *(int *)table->data;
-	int min = *(int *)table->extra1;
-	int max = *(int *)table->extra2;
+	int min = (int)table->min;
+	int max = (int)table->max;
 	struct ctl_table tmp = *table;
 
 	if (write && !capable(CAP_SYS_ADMIN))
@@ -426,10 +420,13 @@ static int
 proc_dolongvec_minmax_bpf_restricted(const struct ctl_table *table, int write,
 				     void *buffer, size_t *lenp, loff_t *ppos)
 {
+	struct ctl_table tmp = *table;
+
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 
-	return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
+	tmp.max = (unsigned long)READ_ONCE(bpf_jit_limit_max);
+	return proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos);
 }
 #endif
 
@@ -440,7 +437,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_mem_pcpu_rsv,
+		.min		= SK_MEMORY_PCPU_RESERVE,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "dev_weight",
@@ -448,7 +446,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_do_dev_weight,
-		.extra1         = SYSCTL_ONE,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "dev_weight_rx_bias",
@@ -456,7 +455,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_do_dev_weight,
-		.extra1         = SYSCTL_ONE,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "dev_weight_tx_bias",
@@ -464,7 +464,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_do_dev_weight,
-		.extra1         = SYSCTL_ONE,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "netdev_max_backlog",
@@ -495,11 +496,11 @@ static struct ctl_table net_core_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax_bpf_enable,
 # ifdef CONFIG_BPF_JIT_ALWAYS_ON
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= 1,
 # else
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 # endif
 	},
 # ifdef CONFIG_HAVE_EBPF_JIT
@@ -509,8 +510,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0600,
 		.proc_handler	= proc_dointvec_minmax_bpf_restricted,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname	= "bpf_jit_kallsyms",
@@ -518,8 +519,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0600,
 		.proc_handler	= proc_dointvec_minmax_bpf_restricted,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 # endif
 	{
@@ -528,8 +529,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(long),
 		.mode		= 0600,
 		.proc_handler	= proc_dolongvec_minmax_bpf_restricted,
-		.extra1		= SYSCTL_LONG_ONE,
-		.extra2		= &bpf_jit_limit_max,
+		.min		= 1,
+		.max		= ULONG_MAX,
 	},
 #endif
 	{
@@ -582,7 +583,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "busy_read",
@@ -590,7 +592,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 #endif
 #ifdef CONFIG_NET_SCHED
@@ -621,8 +624,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &max_skb_frags,
+		.min		= 1,
+		.max		= MAX_SKB_FRAGS,
 	},
 	{
 		.procname	= "netdev_budget_usecs",
@@ -630,7 +633,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &netdev_budget_usecs_min,
+		.min		= 2 * USEC_PER_SEC / HZ,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "fb_tunnels_only_for_init_net",
@@ -638,8 +642,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname	= "devconf_inherit_init_net",
@@ -647,8 +651,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_THREE,
+		.min		= 0,
+		.max		= 3,
 	},
 	{
 		.procname	= "high_order_alloc_disable",
@@ -663,7 +667,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "netdev_unregister_timeout_secs",
@@ -671,8 +676,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &int_3600,
+		.min		= 1,
+		.max		= 3600,
 	},
 	{
 		.procname	= "skb_defer_max",
@@ -680,7 +685,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_do_skb_defer_max,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "neigh_inherit_init_net",
@@ -688,8 +694,8 @@ static struct ctl_table net_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
@@ -707,24 +713,26 @@ static const struct ctl_table netns_core_table[] = {
 		.data		= &init_net.core.sysctl_somaxconn,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.extra1		= SYSCTL_ZERO,
-		.proc_handler	= proc_dointvec_minmax
+		.min		= 0,
+		.proc_handler	= proc_dointvec_minmax,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "optmem_max",
 		.data		= &init_net.core.sysctl_optmem_max,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.extra1		= SYSCTL_ZERO,
-		.proc_handler	= proc_dointvec_minmax
+		.min		= 0,
+		.proc_handler	= proc_dointvec_minmax,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "txrehash",
 		.data		= &init_net.core.sysctl_txrehash,
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 		.proc_handler	= proc_dou8vec_minmax,
 	},
 	{
@@ -740,8 +748,8 @@ static const struct ctl_table netns_core_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "bypass_prot_mem",
@@ -749,8 +757,8 @@ static const struct ctl_table netns_core_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
+		.min		= 0,
+		.max		= 1
 	},
 	/* sysctl_core_net_init() will set the values after this
 	 * to readonly in network namespaces
@@ -761,7 +769,8 @@ static const struct ctl_table netns_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_sndbuf,
+		.min		= SOCK_MIN_SNDBUF,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "rmem_max",
@@ -769,7 +778,8 @@ static const struct ctl_table netns_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_rcvbuf,
+		.min		= SOCK_MIN_RCVBUF,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "wmem_default",
@@ -777,7 +787,8 @@ static const struct ctl_table netns_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_sndbuf,
+		.min		= SOCK_MIN_SNDBUF,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "rmem_default",
@@ -785,7 +796,8 @@ static const struct ctl_table netns_core_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &min_rcvbuf,
+		.min		= SOCK_MIN_RCVBUF,
+		.max		= INT_MAX,
 	},
 };
 
-- 
2.25.1


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

* [RFC PATCH 25/39] sctp: resolve numeric ranges from the current namespace
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (23 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 24/39] net: refresh core sysctl limits without bound pointers wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 26/39] rds: preserve live reconnect limits with numeric bounds wen.yang
                   ` (13 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Read sibling SCTP limits from the current namespace on each call.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/sctp/sysctl.c | 147 +++++++++++++++++++++++-----------------------
 1 file changed, 72 insertions(+), 75 deletions(-)

diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 59c6f22337ea..ed296f568887 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -24,25 +24,6 @@
 #include <net/sctp/structs.h>
 #include <net/sctp/sctp.h>
 #include <linux/sysctl.h>
-#include <linux/limits.h>
-
-static int timer_max = 86400000; /* ms in one day */
-static int sack_timer_min = 1;
-static int sack_timer_max = 500;
-static int addr_scope_max = SCTP_SCOPE_POLICY_MAX;
-static int rwnd_scale_max = 16;
-static int rto_alpha_min = 0;
-static int rto_beta_min = 0;
-static int rto_alpha_max = 1000;
-static int rto_beta_max = 1000;
-static int pf_expose_max = SCTP_PF_EXPOSE_MAX;
-static int ps_retrans_max = SCTP_PS_RETRANS_MAX;
-static int udp_port_max = 65535;
-
-static unsigned long max_autoclose_min = 0;
-static unsigned long max_autoclose_max =
-	(MAX_SCHEDULE_TIMEOUT / HZ > UINT_MAX)
-	? UINT_MAX : MAX_SCHEDULE_TIMEOUT / HZ;
 
 static int proc_sctp_do_hmac_alg(const struct ctl_table *ctl, int write,
 				 void *buffer, size_t *lenp, loff_t *ppos);
@@ -50,6 +31,26 @@ static int proc_sctp_do_rto_min(const struct ctl_table *ctl, int write,
 				void *buffer, size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_rto_max(const struct ctl_table *ctl, int write, void *buffer,
 				size_t *lenp, loff_t *ppos);
+static int proc_sctp_do_pf_retrans(const struct ctl_table *ctl, int write,
+				   void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct net *net = container_of(ctl->data, struct net, sctp.pf_retrans);
+	struct ctl_table tmp = *ctl;
+
+	tmp.max = (unsigned long)READ_ONCE(net->sctp.ps_retrans);
+	return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
+static int proc_sctp_do_ps_retrans(const struct ctl_table *ctl, int write,
+				   void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct net *net = container_of(ctl->data, struct net, sctp.ps_retrans);
+	struct ctl_table tmp = *ctl;
+
+	tmp.min = (unsigned long)READ_ONCE(net->sctp.pf_retrans);
+	return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static int proc_sctp_do_udp_port(const struct ctl_table *ctl, int write, void *buffer,
 				 size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_alpha_beta(const struct ctl_table *ctl, int write,
@@ -65,7 +66,7 @@ static struct ctl_table sctp_table[] = {
 		.data		= &sysctl_sctp_mem,
 		.maxlen		= sizeof(sysctl_sctp_mem),
 		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax
+		.proc_handler	= proc_doulongvec_minmax,
 		.min		= 0,
 		.max		= ULONG_MAX,
 	},
@@ -102,8 +103,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_sctp_do_rto_min,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &init_net.sctp.rto_max
+		.min		= 1,
+		.max		= UINT_MAX,
 	},
 	[SCTP_RTO_MAX_IDX] =  {
 		.procname	= "rto_max",
@@ -111,26 +112,26 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_sctp_do_rto_max,
-		.extra1         = &init_net.sctp.rto_min,
-		.extra2         = &timer_max
+		.min		= 0,
+		.max		= 86400000,
 	},
 	[SCTP_PF_RETRANS_IDX] = {
 		.procname	= "pf_retrans",
 		.data		= &init_net.sctp.pf_retrans,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &init_net.sctp.ps_retrans,
+		.proc_handler	= proc_sctp_do_pf_retrans,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	[SCTP_PS_RETRANS_IDX] = {
 		.procname	= "ps_retrans",
 		.data		= &init_net.sctp.ps_retrans,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &init_net.sctp.pf_retrans,
-		.extra2		= &ps_retrans_max,
+		.proc_handler	= proc_sctp_do_ps_retrans,
+		.min		= (unsigned long)INT_MIN,
+		.max		= SCTP_PS_RETRANS_MAX,
 	},
 	{
 		.procname	= "rto_initial",
@@ -138,8 +139,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &timer_max
+		.min		= 1,
+		.max		= 86400000
 	},
 	{
 		.procname	= "rto_alpha_exp_divisor",
@@ -147,8 +148,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_sctp_do_alpha_beta,
-		.extra1		= &rto_alpha_min,
-		.extra2		= &rto_alpha_max,
+		.min		= 0,
+		.max		= 1000,
 	},
 	{
 		.procname	= "rto_beta_exp_divisor",
@@ -156,8 +157,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_sctp_do_alpha_beta,
-		.extra1		= &rto_beta_min,
-		.extra2		= &rto_beta_max,
+		.min		= 0,
+		.max		= 1000,
 	},
 	{
 		.procname	= "max_burst",
@@ -165,8 +166,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "cookie_preserve_enable",
@@ -188,8 +189,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &timer_max
+		.min		= 1,
+		.max		= 86400000
 	},
 	{
 		.procname	= "sack_timeout",
@@ -197,8 +198,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = &sack_timer_min,
-		.extra2         = &sack_timer_max,
+		.min		= 1,
+		.max		= 500,
 	},
 	{
 		.procname	= "hb_interval",
@@ -206,8 +207,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1         = SYSCTL_ONE,
-		.extra2         = &timer_max
+		.min		= 1,
+		.max		= 86400000
 	},
 	{
 		.procname	= "association_max_retrans",
@@ -215,8 +216,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "path_max_retrans",
@@ -224,8 +225,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "max_init_retransmits",
@@ -233,8 +234,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "sndbuf_policy",
@@ -319,8 +320,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_sctp_do_udp_port,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &udp_port_max,
+		.min		= 0,
+		.max		= 65535,
 	},
 	{
 		.procname	= "encap_port",
@@ -328,8 +329,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &udp_port_max,
+		.min		= 0,
+		.max		= 65535,
 	},
 	{
 		.procname	= "addr_scope_policy",
@@ -337,8 +338,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &addr_scope_max,
+		.min		= 0,
+		.max		= SCTP_SCOPE_POLICY_MAX,
 	},
 	{
 		.procname	= "rwnd_update_shift",
@@ -346,8 +347,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &rwnd_scale_max,
+		.min		= 1,
+		.max		= 16,
 	},
 	{
 		.procname	= "max_autoclose",
@@ -355,8 +356,9 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
 		.proc_handler	= &proc_doulongvec_minmax,
-		.extra1		= &max_autoclose_min,
-		.extra2		= &max_autoclose_max,
+		.min		= 0,
+		.max		= (MAX_SCHEDULE_TIMEOUT / HZ > UINT_MAX)
+				  ? UINT_MAX : MAX_SCHEDULE_TIMEOUT / HZ,
 	},
 #ifdef CONFIG_NET_L3_MASTER_DEV
 	{
@@ -365,8 +367,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #endif
 	{
@@ -382,8 +384,8 @@ static const struct ctl_table sctp_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &pf_expose_max,
+		.min		= 0,
+		.max		= SCTP_PF_EXPOSE_MAX,
 	},
 };
 
@@ -426,8 +428,8 @@ static int proc_sctp_do_rto_min(const struct ctl_table *ctl, int write,
 				void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net *net = container_of(ctl->data, struct net, sctp.rto_min);
-	unsigned int min = *(unsigned int *) ctl->extra1;
-	unsigned int max = *(unsigned int *) ctl->extra2;
+	unsigned int min = ctl->min;
+	unsigned int max = READ_ONCE(net->sctp.rto_max);
 	struct ctl_table tbl;
 	int ret, new_value;
 
@@ -454,8 +456,8 @@ static int proc_sctp_do_rto_max(const struct ctl_table *ctl, int write,
 				void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net *net = container_of(ctl->data, struct net, sctp.rto_max);
-	unsigned int min = *(unsigned int *) ctl->extra1;
-	unsigned int max = *(unsigned int *) ctl->extra2;
+	unsigned int min = READ_ONCE(net->sctp.rto_min);
+	unsigned int max = ctl->max;
 	struct ctl_table tbl;
 	int ret, new_value;
 
@@ -523,8 +525,8 @@ static int proc_sctp_do_udp_port(const struct ctl_table *ctl, int write,
 				 void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct net *net = container_of(ctl->data, struct net, sctp.udp_port);
-	unsigned int min = *(unsigned int *)ctl->extra1;
-	unsigned int max = *(unsigned int *)ctl->extra2;
+	unsigned int min = ctl->min;
+	unsigned int max = ctl->max;
 	struct ctl_table tbl;
 	int ret, new_value;
 
@@ -602,11 +604,6 @@ int sctp_sysctl_net_register(struct net *net)
 	for (i = 0; i < table_size; i++)
 		table[i].data += (char *)(&net->sctp) - (char *)&init_net.sctp;
 
-	table[SCTP_RTO_MIN_IDX].extra2 = &net->sctp.rto_max;
-	table[SCTP_RTO_MAX_IDX].extra1 = &net->sctp.rto_min;
-	table[SCTP_PF_RETRANS_IDX].extra2 = &net->sctp.ps_retrans;
-	table[SCTP_PS_RETRANS_IDX].extra1 = &net->sctp.pf_retrans;
-
 	net->sctp.sysctl_header = register_net_sysctl_sz(net, "net/sctp",
 							 table, table_size);
 	if (net->sctp.sysctl_header == NULL) {
-- 
2.25.1


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

* [RFC PATCH 26/39] rds: preserve live reconnect limits with numeric bounds
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (24 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 25/39] sctp: resolve numeric ranges from the current namespace wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 27/39] ipv4: preserve live and vector bounds with numeric sysctls wen.yang
                   ` (12 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Refresh reconnect bounds while preserving jiffies conversion.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/rds/sysctl.c | 32 +++++++++++++++++++++++++-------
 net/rds/tcp.c    | 11 +++++------
 2 files changed, 30 insertions(+), 13 deletions(-)

diff --git a/net/rds/sysctl.c b/net/rds/sysctl.c
index 025f518a4349..b32213b8ad96 100644
--- a/net/rds/sysctl.c
+++ b/net/rds/sysctl.c
@@ -39,7 +39,6 @@
 static struct ctl_table_header *rds_sysctl_reg_table;
 
 static unsigned long rds_sysctl_reconnect_min = 1;
-static unsigned long rds_sysctl_reconnect_max = ~0UL;
 
 unsigned long rds_sysctl_reconnect_min_jiffies;
 unsigned long rds_sysctl_reconnect_max_jiffies = HZ;
@@ -49,24 +48,43 @@ unsigned int  rds_sysctl_max_unacked_bytes = (16 << 20);
 
 unsigned int rds_sysctl_ping_enable = 1;
 
+static int rds_reconnect_min_handler(const struct ctl_table *table, int write,
+				     void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.min = READ_ONCE(rds_sysctl_reconnect_min);
+	tmp.max = READ_ONCE(rds_sysctl_reconnect_max_jiffies);
+	return proc_doulongvec_ms_jiffies_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
+static int rds_reconnect_max_handler(const struct ctl_table *table, int write,
+				     void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.min = READ_ONCE(rds_sysctl_reconnect_min_jiffies);
+	return proc_doulongvec_ms_jiffies_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static struct ctl_table rds_sysctl_rds_table[] = {
 	{
 		.procname       = "reconnect_min_delay_ms",
 		.data		= &rds_sysctl_reconnect_min_jiffies,
 		.maxlen         = sizeof(unsigned long),
 		.mode           = 0644,
-		.proc_handler   = proc_doulongvec_ms_jiffies_minmax,
-		.extra1		= &rds_sysctl_reconnect_min,
-		.extra2		= &rds_sysctl_reconnect_max_jiffies,
+		.proc_handler   = rds_reconnect_min_handler,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname       = "reconnect_max_delay_ms",
 		.data		= &rds_sysctl_reconnect_max_jiffies,
 		.maxlen         = sizeof(unsigned long),
 		.mode           = 0644,
-		.proc_handler   = proc_doulongvec_ms_jiffies_minmax,
-		.extra1		= &rds_sysctl_reconnect_min_jiffies,
-		.extra2		= &rds_sysctl_reconnect_max,
+		.proc_handler   = rds_reconnect_max_handler,
+		.min		= 0,
+		.max		= ULONG_MAX,
 	},
 	{
 		.procname	= "max_unacked_packets",
diff --git a/net/rds/tcp.c b/net/rds/tcp.c
index 774a71f88d37..4c1d1c3696b2 100644
--- a/net/rds/tcp.c
+++ b/net/rds/tcp.c
@@ -58,9 +58,6 @@ static int rds_tcp_sndbuf_handler(const struct ctl_table *ctl, int write,
 static int rds_tcp_rcvbuf_handler(const struct ctl_table *ctl, int write,
 				  void *buffer, size_t *lenp, loff_t *fpos);
 
-static int rds_tcp_min_sndbuf = SOCK_MIN_SNDBUF;
-static int rds_tcp_min_rcvbuf = SOCK_MIN_RCVBUF;
-
 static struct ctl_table rds_tcp_sysctl_table[] = {
 #define	RDS_TCP_SNDBUF	0
 	{
@@ -69,7 +66,8 @@ static struct ctl_table rds_tcp_sysctl_table[] = {
 		.maxlen         = sizeof(int),
 		.mode           = 0644,
 		.proc_handler   = rds_tcp_sndbuf_handler,
-		.extra1		= &rds_tcp_min_sndbuf,
+		.min		= SOCK_MIN_SNDBUF,
+		.max		= INT_MAX,
 	},
 #define	RDS_TCP_RCVBUF	1
 	{
@@ -78,7 +76,8 @@ static struct ctl_table rds_tcp_sysctl_table[] = {
 		.maxlen         = sizeof(int),
 		.mode           = 0644,
 		.proc_handler   = rds_tcp_rcvbuf_handler,
-		.extra1		= &rds_tcp_min_rcvbuf,
+		.min		= SOCK_MIN_RCVBUF,
+		.max		= INT_MAX,
 	},
 };
 
@@ -764,7 +763,7 @@ static int rds_tcp_skbuf_handler(struct rds_tcp_net *rtn,
 	err = proc_dointvec_minmax(ctl, write, buffer, lenp, fpos);
 	if (err < 0) {
 		pr_warn("Invalid input. Must be >= %d\n",
-			*(int *)(ctl->extra1));
+			(int)ctl->min);
 		return err;
 	}
 
-- 
2.25.1


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

* [RFC PATCH 27/39] ipv4: preserve live and vector bounds with numeric sysctls
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (25 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 26/39] rds: preserve live reconnect limits with numeric bounds wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 28/39] net: refresh fragment and remaining dynamic sysctl bounds wen.yang
                   ` (11 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Use numeric port and group limits and refresh TCP reordering bounds.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/ipv4/sysctl_net_ipv4.c | 334 +++++++++++++++++++------------------
 1 file changed, 169 insertions(+), 165 deletions(-)

diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 2ed9f59ea747..4226ce923534 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -7,7 +7,6 @@
  */
 
 #include <linux/sysctl.h>
-#include <linux/limits.h>
 #include <linux/seqlock.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -21,38 +20,6 @@
 #include <net/protocol.h>
 #include <net/netevent.h>
 
-static int tcp_retr1_max = 255;
-static int ip_local_port_range_min[] = { 1, 1 };
-static int ip_local_port_range_max[] = { 65535, 65535 };
-static int tcp_adv_win_scale_min = -31;
-static int tcp_adv_win_scale_max = 31;
-static int tcp_app_win_max = 31;
-static int tcp_min_snd_mss_min = TCP_MIN_SND_MSS;
-static int tcp_min_snd_mss_max = 65535;
-static int tcp_rto_max_max = TCP_RTO_MAX_SEC * MSEC_PER_SEC;
-static int ip_privileged_port_min;
-static int ip_privileged_port_max = 65535;
-static int ip_ttl_min = 1;
-static int ip_ttl_max = 255;
-static int tcp_syn_retries_min = 1;
-static int tcp_syn_retries_max = MAX_TCP_SYNCNT;
-static int tcp_syn_linear_timeouts_max = MAX_TCP_SYNCNT;
-static unsigned long ip_ping_group_range_min[] = { 0, 0 };
-static unsigned long ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX };
-static u32 u32_max_div_HZ = UINT_MAX / HZ;
-static int one_day_secs = 24 * 3600;
-static u32 fib_multipath_hash_fields_all_mask __maybe_unused =
-	FIB_MULTIPATH_HASH_FIELD_ALL_MASK;
-static unsigned int tcp_child_ehash_entries_max = 16 * 1024 * 1024;
-static unsigned int udp_child_hash_entries_max = UDP_HTABLE_SIZE_MAX;
-static int tcp_plb_max_rounds = 31;
-static int tcp_plb_max_cong_thresh = 256;
-static unsigned int tcp_tw_reuse_delay_max = TCP_PAWS_MSL * MSEC_PER_SEC;
-static int tcp_ecn_mode_max = 5;
-static u32 icmp_errors_extension_mask_all =
-	GENMASK_U8(ICMP_ERR_EXT_COUNT - 1, 0);
-
-static int tcp_min_rcvbuf = 4096;
 
 /* obsolete */
 static int sysctl_tcp_low_latency __read_mostly;
@@ -80,8 +47,8 @@ static int ipv4_local_port_range(const struct ctl_table *table, int write,
 		.data = &range,
 		.maxlen = sizeof(range),
 		.mode = table->mode,
-		.extra1 = &ip_local_port_range_min,
-		.extra2 = &ip_local_port_range_max,
+		.min = 1,
+		.max = 65535,
 	};
 
 	inet_get_local_port_range(net, &range[0], &range[1]);
@@ -116,8 +83,8 @@ static int ipv4_privileged_ports(const struct ctl_table *table, int write,
 		.data = &pports,
 		.maxlen = sizeof(pports),
 		.mode = table->mode,
-		.extra1 = &ip_privileged_port_min,
-		.extra2 = &ip_privileged_port_max,
+		.min = 0,
+		.max = 65535,
 	};
 
 	pports = READ_ONCE(net->ipv4.sysctl_ip_prot_sock);
@@ -178,8 +145,8 @@ static int ipv4_ping_group_range(const struct ctl_table *table, int write,
 		.data = &urange,
 		.maxlen = sizeof(urange),
 		.mode = table->mode,
-		.extra1 = &ip_ping_group_range_min,
-		.extra2 = &ip_ping_group_range_max,
+		.min = 0,
+		.max = GID_T_MAX,
 	};
 
 	inet_get_ping_group_range_table(table, &low, &high);
@@ -530,6 +497,27 @@ static void proc_fib_multipath_hash_set_seed(struct net *net, u32 user_seed)
 
 #endif
 
+static int proc_tcp_reordering(const struct ctl_table *table, int write,
+			       void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct net *net = container_of(table->data, struct net,
+				       ipv4.sysctl_tcp_reordering);
+	struct ctl_table tmp = *table;
+
+	tmp.max = READ_ONCE(net->ipv4.sysctl_tcp_max_reordering);
+	return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
+static int proc_fib_sync_mem(const struct ctl_table *table, int write,
+			     void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct ctl_table tmp = *table;
+
+	tmp.min = READ_ONCE(sysctl_fib_sync_mem_min);
+	tmp.max = READ_ONCE(sysctl_fib_sync_mem_max);
+	return proc_douintvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static struct ctl_table ipv4_table[] = {
 	{
 		.procname	= "tcp_max_orphans",
@@ -625,9 +613,9 @@ static struct ctl_table ipv4_table[] = {
 		.data		= &sysctl_fib_sync_mem,
 		.maxlen		= sizeof(sysctl_fib_sync_mem),
 		.mode		= 0644,
-		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= &sysctl_fib_sync_mem_min,
-		.extra2		= &sysctl_fib_sync_mem_max,
+		.proc_handler	= proc_fib_sync_mem,
+		.min		= 0,
+		.max		= UINT_MAX,
 	},
 };
 
@@ -645,8 +633,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "icmp_echo_enable_probe",
@@ -654,8 +642,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "icmp_echo_ignore_broadcasts",
@@ -663,8 +651,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "icmp_ignore_bogus_error_responses",
@@ -672,8 +660,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "icmp_errors_use_inbound_ifaddr",
@@ -681,8 +669,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "icmp_errors_extension_mask",
@@ -690,8 +678,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &icmp_errors_extension_mask_all,
+		.min		= 0,
+		.max		= GENMASK_U8(ICMP_ERR_EXT_COUNT - 1, 0),
 	},
 	{
 		.procname	= "icmp_ratelimit",
@@ -713,7 +701,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "icmp_msgs_burst",
@@ -721,7 +710,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "ping_group_range",
@@ -737,8 +727,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #endif
 	{
@@ -747,8 +737,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &tcp_ecn_mode_max,
+		.min		= 0,
+		.max		= 5,
 	},
 	{
 		.procname	= "tcp_ecn_option",
@@ -756,8 +746,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_THREE,
+		.min		= 0,
+		.max		= 3,
 	},
 	{
 		.procname	= "tcp_ecn_option_beacon",
@@ -765,8 +755,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_THREE,
+		.min		= 0,
+		.max		= 3,
 	},
 	{
 		.procname	= "tcp_ecn_fallback",
@@ -774,8 +764,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "ip_dynaddr",
@@ -801,8 +791,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
-		.min           = 0,
-		.max           = U8_MAX,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname       = "tcp_early_demux",
@@ -810,8 +800,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
-		.min           = 0,
-		.max           = U8_MAX,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname       = "nexthop_compat_mode",
@@ -819,8 +809,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "ip_default_ttl",
@@ -828,8 +818,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= &ip_ttl_min,
-		.extra2		= &ip_ttl_max,
+		.min		= 1,
+		.max		= 255,
 	},
 	{
 		.procname	= "ip_local_port_range",
@@ -876,8 +866,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler   = ipv4_fwd_update_priority,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "ip_nonlocal_bind",
@@ -894,8 +884,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "fwmark_reflect",
@@ -922,8 +912,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #endif
 	{
@@ -948,8 +938,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &tcp_min_snd_mss_min,
-		.extra2		= &tcp_min_snd_mss_max,
+		.min		= TCP_MIN_SND_MSS,
+		.max		= 65535,
 	},
 	{
 		.procname	= "tcp_mtu_probe_floor",
@@ -957,8 +947,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &tcp_min_snd_mss_min,
-		.extra2		= &tcp_min_snd_mss_max,
+		.min		= TCP_MIN_SND_MSS,
+		.max		= 65535,
 	},
 	{
 		.procname	= "tcp_probe_threshold",
@@ -973,7 +963,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u32),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra2		= &u32_max_div_HZ,
+		.max		= UINT_MAX / HZ,
+		.min		= 0,
 	},
 	{
 		.procname	= "igmp_link_local_mcast_reports",
@@ -1005,7 +996,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 #endif
 	{
@@ -1056,8 +1048,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= &tcp_syn_retries_min,
-		.extra2		= &tcp_syn_retries_max
+		.min		= 1,
+		.max		= MAX_TCP_SYNCNT
 	},
 	{
 		.procname	= "tcp_synack_retries",
@@ -1085,17 +1077,17 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE
+		.min		= 0,
+		.max		= 1
 	},
 	{
 		.procname	= "tcp_reordering",
 		.data		= &init_net.ipv4.sysctl_tcp_reordering,
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &init_net.ipv4.sysctl_tcp_max_reordering,
+		.proc_handler	= proc_tcp_reordering,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "tcp_retries1",
@@ -1103,7 +1095,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra2		= &tcp_retr1_max
+		.max		= 255,
+		.min		= 0,
 	},
 	{
 		.procname	= "tcp_retries2",
@@ -1143,8 +1136,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname	= "tcp_tw_reuse_delay",
@@ -1152,8 +1145,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &tcp_tw_reuse_delay_max,
+		.min		= 1,
+		.max		= TCP_PAWS_MSL * MSEC_PER_SEC,
 	},
 	{
 		.procname	= "tcp_max_syn_backlog",
@@ -1187,7 +1180,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_tfo_blackhole_detect_timeout,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 	{
@@ -1196,8 +1190,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "fib_multipath_hash_policy",
@@ -1205,8 +1199,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_fib_multipath_hash_policy,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_THREE,
+		.min		= 0,
+		.max		= 3,
 	},
 	{
 		.procname	= "fib_multipath_hash_fields",
@@ -1214,8 +1208,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u32),
 		.mode		= 0644,
 		.proc_handler	= proc_fib_multipath_hash_fields,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &fib_multipath_hash_fields_all_mask,
+		.min		= 1,
+		.max		= FIB_MULTIPATH_HASH_FIELD_ALL_MASK,
 	},
 	{
 		.procname	= "fib_multipath_hash_seed",
@@ -1223,6 +1217,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u32),
 		.mode		= 0644,
 		.proc_handler	= proc_fib_multipath_hash_seed,
+		.min		= 0,
+		.max		= UINT_MAX,
 	},
 #endif
 	{
@@ -1239,8 +1235,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #endif
 	{
@@ -1276,8 +1272,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_FOUR,
+		.min		= 0,
+		.max		= 4,
 	},
 	{
 		.procname	= "tcp_recovery",
@@ -1294,8 +1290,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
-		.min           = 0,
-		.max           = U8_MAX,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_slow_start_after_idle",
@@ -1357,7 +1353,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "tcp_dsack",
@@ -1374,8 +1371,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &tcp_app_win_max,
+		.min		= 0,
+		.max		= 31,
 	},
 	{
 		.procname	= "tcp_adv_win_scale",
@@ -1383,8 +1380,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &tcp_adv_win_scale_min,
-		.extra2		= &tcp_adv_win_scale_max,
+		.min		= (unsigned long)-31,
+		.max		= 31,
 	},
 	{
 		.procname	= "tcp_frto",
@@ -1410,8 +1407,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "tcp_moderate_rcvbuf",
@@ -1428,8 +1425,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_INT_MAX,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "tcp_tso_win_divisor",
@@ -1469,7 +1466,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_tso_rtt_log",
@@ -1486,8 +1484,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &one_day_secs
+		.min		= 0,
+		.max		= 24 * 3600
 	},
 	{
 		.procname	= "tcp_autocorking",
@@ -1495,8 +1493,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "tcp_invalid_ratelimit",
@@ -1511,8 +1509,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE_THOUSAND,
+		.min		= 0,
+		.max		= 1000,
 	},
 	{
 		.procname	= "tcp_pacing_ca_ratio",
@@ -1520,8 +1518,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE_THOUSAND,
+		.min		= 0,
+		.max		= 1000,
 	},
 	{
 		.procname	= "tcp_wmem",
@@ -1529,7 +1527,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(init_net.ipv4.sysctl_tcp_wmem),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "tcp_rmem",
@@ -1537,7 +1536,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(init_net.ipv4.sysctl_tcp_rmem),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &tcp_min_rcvbuf,
+		.min		= 4096,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "tcp_comp_sack_delay_ns",
@@ -1554,8 +1554,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= SYSCTL_ONE_THOUSAND,
+		.min		= 1,
+		.max		= 1000,
 	},
 	{
 		.procname	= "tcp_comp_sack_slack_ns",
@@ -1572,7 +1572,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_backlog_ack_defer",
@@ -1580,8 +1581,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname       = "tcp_reflect_tos",
@@ -1589,8 +1590,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "tcp_ehash_entries",
@@ -1604,8 +1605,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &tcp_child_ehash_entries_max,
+		.min		= 0,
+		.max		= 16 * 1024 * 1024,
 	},
 	{
 		.procname	= "udp_hash_entries",
@@ -1619,8 +1620,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &udp_child_hash_entries_max,
+		.min		= 0,
+		.max		= UDP_HTABLE_SIZE_MAX,
 	},
 	{
 		.procname	= "udp_rmem_min",
@@ -1628,7 +1629,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(init_net.ipv4.sysctl_udp_rmem_min),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "udp_wmem_min",
@@ -1636,7 +1638,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(init_net.ipv4.sysctl_udp_wmem_min),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "fib_notify_on_flag_change",
@@ -1644,8 +1647,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname       = "tcp_plb_enabled",
@@ -1653,8 +1656,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname       = "tcp_plb_idle_rehash_rounds",
@@ -1662,7 +1665,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
-		.extra2		= &tcp_plb_max_rounds,
+		.max		= 31,
+		.min		= 0,
 	},
 	{
 		.procname       = "tcp_plb_rehash_rounds",
@@ -1670,7 +1674,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
-		.extra2         = &tcp_plb_max_rounds,
+		.max		= 31,
+		.min		= 0,
 	},
 	{
 		.procname       = "tcp_plb_suspend_rto_sec",
@@ -1678,8 +1683,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(u8),
 		.mode           = 0644,
 		.proc_handler   = proc_dou8vec_minmax,
-		.min           = 0,
-		.max           = U8_MAX,
+		.min		= 0,
+		.max		= U8_MAX,
 	},
 	{
 		.procname       = "tcp_plb_cong_thresh",
@@ -1687,8 +1692,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen         = sizeof(int),
 		.mode           = 0644,
 		.proc_handler   = proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = &tcp_plb_max_cong_thresh,
+		.min		= 0,
+		.max		= 256,
 	},
 	{
 		.procname	= "tcp_syn_linear_timeouts",
@@ -1696,8 +1701,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &tcp_syn_linear_timeouts_max,
+		.min		= 0,
+		.max		= MAX_TCP_SYNCNT,
 	},
 	{
 		.procname	= "tcp_shrink_window",
@@ -1705,8 +1710,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "tcp_pingpong_thresh",
@@ -1714,7 +1719,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= U8_MAX,
 	},
 	{
 		.procname	= "tcp_rto_min_us",
@@ -1722,7 +1728,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
+		.min		= 1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "tcp_rto_max_ms",
@@ -1730,8 +1737,8 @@ static const struct ctl_table ipv4_net_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE_THOUSAND,
-		.extra2		= &tcp_rto_max_max,
+		.min		= 1000,
+		.max		= TCP_RTO_MAX_SEC * MSEC_PER_SEC,
 	},
 };
 
@@ -1757,9 +1764,6 @@ static const struct ctl_table *ipv4_net_table_dup(struct net *net)
 			 */
 			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;
 }
-- 
2.25.1


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

* [RFC PATCH 28/39] net: refresh fragment and remaining dynamic sysctl bounds
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (26 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 27/39] ipv4: preserve live and vector bounds with numeric sysctls wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 29/39] neighbour: derive sysctl ownership independently of bounds wen.yang
                   ` (10 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Refresh fragment sibling thresholds and use numeric Phonet bounds.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/ieee802154/6lowpan/reassembly.c     | 27 +++++++++++++++++----
 net/ipv4/ip_fragment.c                  | 31 +++++++++++++++++++------
 net/ipv6/netfilter/nf_conntrack_reasm.c | 27 +++++++++++++++++----
 net/ipv6/reassembly.c                   | 27 +++++++++++++++++----
 net/phonet/sysctl.c                     |  6 ++---
 5 files changed, 92 insertions(+), 26 deletions(-)

diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
index a2167f76ecc3..00b328378d19 100644
--- a/net/ieee802154/6lowpan/reassembly.c
+++ b/net/ieee802154/6lowpan/reassembly.c
@@ -10,7 +10,6 @@
 #define pr_fmt(fmt) "6LoWPAN: " fmt
 
 #include <linux/net.h>
-#include <linux/limits.h>
 #include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/random.h>
@@ -327,12 +326,32 @@ int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type)
 
 #ifdef CONFIG_SYSCTL
 
+static int lowpan_frags_high_thresh_handler(const struct ctl_table *table,
+					    int write, void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct fqdir *fqdir = container_of(table->data, struct fqdir, high_thresh);
+	struct ctl_table tmp = *table;
+
+	tmp.min = READ_ONCE(fqdir->low_thresh);
+	return proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
+static int lowpan_frags_low_thresh_handler(const struct ctl_table *table,
+					   int write, void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct fqdir *fqdir = container_of(table->data, struct fqdir, low_thresh);
+	struct ctl_table tmp = *table;
+
+	tmp.max = READ_ONCE(fqdir->high_thresh);
+	return proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static struct ctl_table lowpan_frags_ns_ctl_table[] = {
 	{
 		.procname	= "6lowpanfrag_high_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
+		.proc_handler	= lowpan_frags_high_thresh_handler,
 		.min		= 0,
 		.max		= ULONG_MAX,
 	},
@@ -340,7 +359,7 @@ static struct ctl_table lowpan_frags_ns_ctl_table[] = {
 		.procname	= "6lowpanfrag_low_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
+		.proc_handler	= lowpan_frags_low_thresh_handler,
 		.min		= 0,
 		.max		= ULONG_MAX,
 	},
@@ -385,9 +404,7 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
 	}
 
 	table[0].data	= &ieee802154_lowpan->fqdir->high_thresh;
-	table[0].extra1	= &ieee802154_lowpan->fqdir->low_thresh;
 	table[1].data	= &ieee802154_lowpan->fqdir->low_thresh;
-	table[1].extra2	= &ieee802154_lowpan->fqdir->high_thresh;
 	table[2].data	= &ieee802154_lowpan->fqdir->timeout;
 
 	hdr = register_net_sysctl_sz(net, "net/ieee802154/6lowpan", table,
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 75bc675e5579..c01df071240a 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -24,7 +24,6 @@
 #define pr_fmt(fmt) "IPv4: " fmt
 
 #include <linux/compiler.h>
-#include <linux/limits.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/mm.h>
@@ -544,14 +543,33 @@ struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user)
 EXPORT_SYMBOL(ip_check_defrag);
 
 #ifdef CONFIG_SYSCTL
-static int dist_min;
+
+static int ip4_frags_high_thresh_handler(const struct ctl_table *table,
+					 int write, void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct fqdir *fqdir = container_of(table->data, struct fqdir, high_thresh);
+	struct ctl_table tmp = *table;
+
+	tmp.min = READ_ONCE(fqdir->low_thresh);
+	return proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
+static int ip4_frags_low_thresh_handler(const struct ctl_table *table,
+					int write, void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct fqdir *fqdir = container_of(table->data, struct fqdir, low_thresh);
+	struct ctl_table tmp = *table;
+
+	tmp.max = READ_ONCE(fqdir->high_thresh);
+	return proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
 
 static struct ctl_table ip4_frags_ns_ctl_table[] = {
 	{
 		.procname	= "ipfrag_high_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
+		.proc_handler	= ip4_frags_high_thresh_handler,
 		.min		= 0,
 		.max		= ULONG_MAX,
 	},
@@ -559,7 +577,7 @@ static struct ctl_table ip4_frags_ns_ctl_table[] = {
 		.procname	= "ipfrag_low_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
+		.proc_handler	= ip4_frags_low_thresh_handler,
 		.min		= 0,
 		.max		= ULONG_MAX,
 	},
@@ -574,7 +592,8 @@ static struct ctl_table ip4_frags_ns_ctl_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &dist_min,
+		.min		= 0,
+		.max		= INT_MAX,
 	},
 };
 
@@ -603,9 +622,7 @@ static int __net_init ip4_frags_ns_ctl_register(struct net *net)
 
 	}
 	table[0].data	= &net->ipv4.fqdir->high_thresh;
-	table[0].extra1	= &net->ipv4.fqdir->low_thresh;
 	table[1].data	= &net->ipv4.fqdir->low_thresh;
-	table[1].extra2	= &net->ipv4.fqdir->high_thresh;
 	table[2].data	= &net->ipv4.fqdir->timeout;
 	table[3].data	= &net->ipv4.fqdir->max_dist;
 
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 1f3ba85f6dca..4cb69dc14b11 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -13,7 +13,6 @@
 #define pr_fmt(fmt) "IPv6-nf: " fmt
 
 #include <linux/errno.h>
-#include <linux/limits.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/net.h>
@@ -44,6 +43,26 @@ static struct nft_ct_frag6_pernet *nf_frag_pernet(struct net *net)
 
 #ifdef CONFIG_SYSCTL
 
+static int nf_ct_frag6_high_thresh_handler(const struct ctl_table *table,
+					   int write, void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct fqdir *fqdir = container_of(table->data, struct fqdir, high_thresh);
+	struct ctl_table tmp = *table;
+
+	tmp.min = READ_ONCE(fqdir->low_thresh);
+	return proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
+static int nf_ct_frag6_low_thresh_handler(const struct ctl_table *table,
+					  int write, void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct fqdir *fqdir = container_of(table->data, struct fqdir, low_thresh);
+	struct ctl_table tmp = *table;
+
+	tmp.max = READ_ONCE(fqdir->high_thresh);
+	return proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static struct ctl_table nf_ct_frag6_sysctl_table[] = {
 	{
 		.procname	= "nf_conntrack_frag6_timeout",
@@ -55,7 +74,7 @@ static struct ctl_table nf_ct_frag6_sysctl_table[] = {
 		.procname	= "nf_conntrack_frag6_low_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
+		.proc_handler	= nf_ct_frag6_low_thresh_handler,
 		.min		= 0,
 		.max		= ULONG_MAX,
 	},
@@ -63,7 +82,7 @@ static struct ctl_table nf_ct_frag6_sysctl_table[] = {
 		.procname	= "nf_conntrack_frag6_high_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
+		.proc_handler	= nf_ct_frag6_high_thresh_handler,
 		.min		= 0,
 		.max		= ULONG_MAX,
 	},
@@ -87,9 +106,7 @@ static int nf_ct_frag6_sysctl_register(struct net *net)
 
 	table[0].data	= &nf_frag->fqdir->timeout;
 	table[1].data	= &nf_frag->fqdir->low_thresh;
-	table[1].extra2	= &nf_frag->fqdir->high_thresh;
 	table[2].data	= &nf_frag->fqdir->high_thresh;
-	table[2].extra1	= &nf_frag->fqdir->low_thresh;
 
 	hdr = register_net_sysctl_sz(net, "net/netfilter", table,
 				     ARRAY_SIZE(nf_ct_frag6_sysctl_table));
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 70c845385744..99b819f0cbf5 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -26,7 +26,6 @@
 #define pr_fmt(fmt) "IPv6: " fmt
 
 #include <linux/errno.h>
-#include <linux/limits.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/socket.h>
@@ -422,12 +421,32 @@ static const struct inet6_protocol frag_protocol = {
 
 #ifdef CONFIG_SYSCTL
 
+static int ip6_frags_high_thresh_handler(const struct ctl_table *table,
+					 int write, void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct fqdir *fqdir = container_of(table->data, struct fqdir, high_thresh);
+	struct ctl_table tmp = *table;
+
+	tmp.min = READ_ONCE(fqdir->low_thresh);
+	return proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
+static int ip6_frags_low_thresh_handler(const struct ctl_table *table,
+					int write, void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct fqdir *fqdir = container_of(table->data, struct fqdir, low_thresh);
+	struct ctl_table tmp = *table;
+
+	tmp.max = READ_ONCE(fqdir->high_thresh);
+	return proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos);
+}
+
 static struct ctl_table ip6_frags_ns_ctl_table[] = {
 	{
 		.procname	= "ip6frag_high_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
+		.proc_handler	= ip6_frags_high_thresh_handler,
 		.min		= 0,
 		.max		= ULONG_MAX,
 	},
@@ -435,7 +454,7 @@ static struct ctl_table ip6_frags_ns_ctl_table[] = {
 		.procname	= "ip6frag_low_thresh",
 		.maxlen		= sizeof(unsigned long),
 		.mode		= 0644,
-		.proc_handler	= proc_doulongvec_minmax,
+		.proc_handler	= ip6_frags_low_thresh_handler,
 		.min		= 0,
 		.max		= ULONG_MAX,
 	},
@@ -472,9 +491,7 @@ static int __net_init ip6_frags_ns_sysctl_register(struct net *net)
 
 	}
 	table[0].data	= &net->ipv6.fqdir->high_thresh;
-	table[0].extra1	= &net->ipv6.fqdir->low_thresh;
 	table[1].data	= &net->ipv6.fqdir->low_thresh;
-	table[1].extra2	= &net->ipv6.fqdir->high_thresh;
 	table[2].data	= &net->ipv6.fqdir->timeout;
 
 	hdr = register_net_sysctl_sz(net, "net/ipv6", table,
diff --git a/net/phonet/sysctl.c b/net/phonet/sysctl.c
index 463a74a27d3e..fe2893b07e2a 100644
--- a/net/phonet/sysctl.c
+++ b/net/phonet/sysctl.c
@@ -22,8 +22,6 @@
 #define DYNAMIC_PORT_MAX	0x7f
 
 static DEFINE_SEQLOCK(local_port_range_lock);
-static int local_port_range_min[2] = {0, 0};
-static int local_port_range_max[2] = {1023, 1023};
 static int local_port_range[2] = {DYNAMIC_PORT_MIN, DYNAMIC_PORT_MAX};
 static struct ctl_table_header *phonet_table_hrd;
 
@@ -57,8 +55,8 @@ static int proc_local_port_range(const struct ctl_table *table, int write,
 		.data = &range,
 		.maxlen = sizeof(range),
 		.mode = table->mode,
-		.extra1 = &local_port_range_min,
-		.extra2 = &local_port_range_max,
+		.min = 0,
+		.max = 1023,
 	};
 
 	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
-- 
2.25.1


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

* [RFC PATCH 29/39] neighbour: derive sysctl ownership independently of bounds
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (27 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 28/39] net: refresh fragment and remaining dynamic sysctl bounds wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 30/39] net: resolve sysctl owners from their backing data wen.yang
                   ` (9 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Derive neighbour parameters from backing storage instead of extra pointers.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 include/net/neighbour.h |   3 ++
 net/core/neighbour.c    | 101 ++++++++++++++++++++++++----------------
 2 files changed, 64 insertions(+), 40 deletions(-)

diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 79a5a548a129..316e34e97fcc 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -420,6 +420,9 @@ void *neigh_seq_start(struct seq_file *, loff_t *, struct neigh_table *,
 void *neigh_seq_next(struct seq_file *, void *, loff_t *);
 void neigh_seq_stop(struct seq_file *, void *);
 
+/* For neighbour parameter entries, whose data points into parms->data. */
+struct neigh_parms *neigh_sysctl_parms(const struct ctl_table *ctl);
+
 int neigh_proc_dointvec(const struct ctl_table *ctl, int write,
 			void *buffer, size_t *lenp, loff_t *ppos);
 int neigh_proc_dointvec_jiffies(const struct ctl_table *ctl, int write,
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index d6e9c7c59a89..ab2559500134 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -3627,16 +3627,14 @@ void neigh_app_ns(struct neighbour *n)
 }
 
 #ifdef CONFIG_SYSCTL
-static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN);
-
 static int proc_unres_qlen(const struct ctl_table *ctl, int write,
 			   void *buffer, size_t *lenp, loff_t *ppos)
 {
 	int size, ret;
 	struct ctl_table tmp = *ctl;
 
-	tmp.extra1 = SYSCTL_ZERO;
-	tmp.extra2 = &unres_qlen_max;
+	tmp.min = 0;
+	tmp.max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN);
 	tmp.data = &size;
 
 	size = *(int *)ctl->data / SKB_TRUESIZE(ETH_FRAME_LEN);
@@ -3666,8 +3664,8 @@ static void neigh_copy_dflt_parms(struct net *net, struct neigh_parms *p,
 
 static void neigh_proc_update(const struct ctl_table *ctl, int write)
 {
-	struct net_device *dev = ctl->extra1;
-	struct neigh_parms *p = ctl->extra2;
+	struct neigh_parms *p = neigh_sysctl_parms(ctl);
+	struct net_device *dev = p->dev;
 	struct net *net = neigh_parms_net(p);
 	int index = (int *) ctl->data - p->data;
 
@@ -3688,8 +3686,8 @@ static int neigh_proc_dointvec_zero_intmax(const struct ctl_table *ctl, int writ
 	struct ctl_table tmp = *ctl;
 	int ret;
 
-	tmp.extra1 = SYSCTL_ZERO;
-	tmp.extra2 = SYSCTL_INT_MAX;
+	tmp.min = 0;
+	tmp.max = INT_MAX;
 
 	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 	neigh_proc_update(ctl, write);
@@ -3700,13 +3698,10 @@ static int neigh_proc_dointvec_ms_jiffies_positive(const struct ctl_table *ctl,
 						   void *buffer, size_t *lenp, loff_t *ppos)
 {
 	struct ctl_table tmp = *ctl;
-	int ret, min, max;
-
-	min = msecs_to_jiffies(NTBL_PARM_MS_MIN);
-	max = msecs_to_jiffies(NTBL_PARM_MS_MAX);
+	int ret;
 
-	tmp.extra1 = &min;
-	tmp.extra2 = &max;
+	tmp.min = msecs_to_jiffies(NTBL_PARM_MS_MIN);
+	tmp.max = msecs_to_jiffies(NTBL_PARM_MS_MAX);
 
 	ret = proc_dointvec_ms_jiffies_minmax(&tmp, write, buffer, lenp, ppos);
 	neigh_proc_update(ctl, write);
@@ -3764,7 +3759,7 @@ static int neigh_proc_base_reachable_time(const struct ctl_table *ctl, int write
 					  void *buffer, size_t *lenp,
 					  loff_t *ppos)
 {
-	struct neigh_parms *p = ctl->extra2;
+	struct neigh_parms *p = neigh_sysctl_parms(ctl);
 	int ret;
 
 	if (strcmp(ctl->procname, "base_reachable_time") == 0)
@@ -3784,35 +3779,31 @@ static int neigh_proc_base_reachable_time(const struct ctl_table *ctl, int write
 	return ret;
 }
 
-#define NEIGH_PARMS_DATA_OFFSET(index)	\
-	(&((struct neigh_parms *) 0)->data[index])
-
-#define NEIGH_SYSCTL_ENTRY(attr, data_attr, name, mval, proc) \
+#define NEIGH_SYSCTL_ENTRY(attr, name, mval, proc) \
 	[NEIGH_VAR_ ## attr] = { \
 		.procname	= name, \
-		.data		= NEIGH_PARMS_DATA_OFFSET(NEIGH_VAR_ ## data_attr), \
 		.maxlen		= sizeof(int), \
 		.mode		= mval, \
 		.proc_handler	= proc, \
 	}
 
 #define NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(attr, name) \
-	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_zero_intmax)
+	NEIGH_SYSCTL_ENTRY(attr, name, 0644, neigh_proc_dointvec_zero_intmax)
 
 #define NEIGH_SYSCTL_JIFFIES_ENTRY(attr, name) \
-	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_jiffies)
+	NEIGH_SYSCTL_ENTRY(attr, name, 0644, neigh_proc_dointvec_jiffies)
 
 #define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \
-	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies)
+	NEIGH_SYSCTL_ENTRY(attr, name, 0644, neigh_proc_dointvec_userhz_jiffies)
 
 #define NEIGH_SYSCTL_MS_JIFFIES_POSITIVE_ENTRY(attr, name) \
-	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_ms_jiffies_positive)
+	NEIGH_SYSCTL_ENTRY(attr, name, 0644, neigh_proc_dointvec_ms_jiffies_positive)
 
-#define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \
-	NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies)
+#define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, name) \
+	NEIGH_SYSCTL_ENTRY(attr, name, 0644, neigh_proc_dointvec_ms_jiffies)
 
-#define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, data_attr, name) \
-	NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_unres_qlen)
+#define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, name) \
+	NEIGH_SYSCTL_ENTRY(attr, name, 0644, neigh_proc_dointvec_unres_qlen)
 
 static struct neigh_sysctl_table {
 	struct ctl_table_header *sysctl_header;
@@ -3834,9 +3825,9 @@ static struct neigh_sysctl_table {
 		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(ANYCAST_DELAY, "anycast_delay"),
 		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(PROXY_DELAY, "proxy_delay"),
 		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(LOCKTIME, "locktime"),
-		NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, QUEUE_LEN_BYTES, "unres_qlen"),
-		NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, RETRANS_TIME, "retrans_time_ms"),
-		NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(BASE_REACHABLE_TIME_MS, BASE_REACHABLE_TIME, "base_reachable_time_ms"),
+		NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, "unres_qlen"),
+		NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, "retrans_time_ms"),
+		NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(BASE_REACHABLE_TIME_MS, "base_reachable_time_ms"),
 		[NEIGH_VAR_GC_INTERVAL] = {
 			.procname	= "gc_interval",
 			.maxlen		= sizeof(int),
@@ -3847,29 +3838,61 @@ static struct neigh_sysctl_table {
 			.procname	= "gc_thresh1",
 			.maxlen		= sizeof(int),
 			.mode		= 0644,
-			.extra1		= SYSCTL_ZERO,
-			.extra2		= SYSCTL_INT_MAX,
+			.min		= 0,
+			.max		= INT_MAX,
 			.proc_handler	= proc_dointvec_minmax,
 		},
 		[NEIGH_VAR_GC_THRESH2] = {
 			.procname	= "gc_thresh2",
 			.maxlen		= sizeof(int),
 			.mode		= 0644,
-			.extra1		= SYSCTL_ZERO,
-			.extra2		= SYSCTL_INT_MAX,
+			.min		= 0,
+			.max		= INT_MAX,
 			.proc_handler	= proc_dointvec_minmax,
 		},
 		[NEIGH_VAR_GC_THRESH3] = {
 			.procname	= "gc_thresh3",
 			.maxlen		= sizeof(int),
 			.mode		= 0644,
-			.extra1		= SYSCTL_ZERO,
-			.extra2		= SYSCTL_INT_MAX,
+			.min		= 0,
+			.max		= INT_MAX,
 			.proc_handler	= proc_dointvec_minmax,
 		},
 	},
 };
 
+static unsigned int neigh_sysctl_data_index(unsigned int index)
+{
+	switch (index) {
+	case NEIGH_VAR_QUEUE_LEN:
+		return NEIGH_VAR_QUEUE_LEN_BYTES;
+	case NEIGH_VAR_RETRANS_TIME_MS:
+		return NEIGH_VAR_RETRANS_TIME;
+	case NEIGH_VAR_BASE_REACHABLE_TIME_MS:
+		return NEIGH_VAR_BASE_REACHABLE_TIME;
+	default:
+		return index;
+	}
+}
+
+struct neigh_parms *neigh_sysctl_parms(const struct ctl_table *ctl)
+{
+	unsigned int i;
+
+	/* The aliases use the same storage as their non-millisecond entries.
+	 * Keep data pointing at that storage, including for ndisc handlers.
+	 */
+	for (i = 0; i < NEIGH_VAR_GC_INTERVAL; i++) {
+		if (!strcmp(ctl->procname,
+			    neigh_sysctl_template.neigh_vars[i].procname))
+			return (struct neigh_parms *)
+				((char *)ctl->data - offsetof(struct neigh_parms, data) -
+				 neigh_sysctl_data_index(i) * sizeof(int));
+	}
+	return NULL;
+}
+EXPORT_SYMBOL(neigh_sysctl_parms);
+
 int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
 			  proc_handler *handler)
 {
@@ -3885,9 +3908,7 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
 		goto err;
 
 	for (i = 0; i < NEIGH_VAR_GC_INTERVAL; i++) {
-		t->neigh_vars[i].data += (long) p;
-		t->neigh_vars[i].extra1 = dev;
-		t->neigh_vars[i].extra2 = p;
+		t->neigh_vars[i].data = &p->data[neigh_sysctl_data_index(i)];
 	}
 
 	neigh_vars_size = ARRAY_SIZE(t->neigh_vars);
-- 
2.25.1


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

* [RFC PATCH 30/39] net: resolve sysctl owners from their backing data
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (28 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 29/39] neighbour: derive sysctl ownership independently of bounds wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 31/39] ipvs: derive sysctl ownership from numeric storage wen.yang
                   ` (8 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Derive network owners from backing storage and remove extra-pointer use.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 drivers/net/vrf.c  | 20 +++++++------------
 net/ipv4/route.c   |  8 ++++----
 net/ipv6/ndisc.c   |  3 ++-
 net/ipv6/route.c   | 11 ++++++-----
 net/mpls/af_mpls.c | 49 ++++++++++++++++------------------------------
 5 files changed, 36 insertions(+), 55 deletions(-)

diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 79d433f49f80..993cc2d120fd 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -1826,22 +1826,17 @@ static int vrf_strict_mode_change(struct vrf_map *vmap, bool new_mode)
 static int vrf_shared_table_handler(const struct ctl_table *table, int write,
 				    void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct net *net = (struct net *)table->extra1;
+	struct net *net = table->data;
 	struct vrf_map *vmap = netns_vrf_map(net);
 	int proc_strict_mode = 0;
-	struct ctl_table tmp = {
-		.procname	= table->procname,
-		.data		= &proc_strict_mode,
-		.maxlen		= sizeof(int),
-		.mode		= table->mode,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
-	};
+	struct ctl_table tmp = *table;
 	int ret;
 
 	if (!write)
 		proc_strict_mode = vrf_strict_mode(vmap);
 
+	tmp.data = &proc_strict_mode;
+	tmp.maxlen = sizeof(proc_strict_mode);
 	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 
 	if (write && ret == 0)
@@ -1857,8 +1852,8 @@ static const struct ctl_table vrf_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= vrf_shared_table_handler,
-		/* set by the vrf_netns_init */
-		.extra1		= NULL,
+		.min		= 0,
+		.max		= 1,
 	},
 };
 
@@ -1870,8 +1865,7 @@ static int vrf_netns_init_sysctl(struct net *net, struct netns_vrf *nn_vrf)
 	if (!table)
 		return -ENOMEM;
 
-	/* init the extra1 parameter with the reference to current netns */
-	table[0].extra1 = net;
+	table[0].data = net;
 
 	nn_vrf->ctl_hdr = register_net_sysctl_sz(net, "net/vrf", table,
 						 ARRAY_SIZE(vrf_table));
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 595a3b7f1783..b4c415f720b2 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -3507,12 +3507,11 @@ void ip_rt_multicast_event(struct in_device *in_dev)
 static int ip_rt_gc_interval __read_mostly  = 60 * HZ;
 static int ip_rt_gc_min_interval __read_mostly	= HZ / 2;
 static int ip_rt_gc_elasticity __read_mostly	= 8;
-static int ip_min_valid_pmtu __read_mostly	= IPV4_MIN_MTU;
 
 static int ipv4_sysctl_rtcache_flush(const struct ctl_table *__ctl, int write,
 		void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct net *net = (struct net *)__ctl->extra1;
+	struct net *net = __ctl->data;
 
 	if (write) {
 		rt_cache_flush(net);
@@ -3627,7 +3626,8 @@ static struct ctl_table ipv4_route_netns_table[] = {
 		.maxlen         = sizeof(int),
 		.mode           = 0644,
 		.proc_handler   = proc_dointvec_minmax,
-		.extra1         = &ip_min_valid_pmtu,
+		.min            = IPV4_MIN_MTU,
+		.max            = INT_MAX,
 	},
 	{
 		.procname       = "mtu_expires",
@@ -3670,7 +3670,7 @@ static __net_init int sysctl_route_net_init(struct net *net)
 		for (i = 1; i < table_size; i++)
 			tbl[i].data += (void *)net - (void *)&init_net;
 	}
-	tbl[0].extra1 = net;
+	tbl[0].data = net;
 
 	net->ipv4.route_hdr = register_net_sysctl_sz(net, "net/ipv4/route",
 						     tbl, table_size);
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 0ed1a619372b..956220ad0cf8 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1956,7 +1956,8 @@ static void ndisc_warn_deprecated_sysctl(const struct ctl_table *ctl,
 int ndisc_ifinfo_sysctl_change(const struct ctl_table *ctl, int write, void *buffer,
 		size_t *lenp, loff_t *ppos)
 {
-	struct net_device *dev = ctl->extra1;
+	struct neigh_parms *p = neigh_sysctl_parms(ctl);
+	struct net_device *dev = p->dev;
 	struct inet6_dev *idev;
 	int ret;
 
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 94e5ef1c4f83..a6925e9750e0 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -6563,9 +6563,12 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v)
 static int ipv6_sysctl_rtcache_flush(const struct ctl_table *ctl, int write,
 			      void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct net *net;
+	int *valp = ctl->data;
+	struct net *net = container_of(valp, struct net,
+				       ipv6.sysctl.flush_delay);
 	int delay;
 	int ret;
+
 	if (!write)
 		return -EINVAL;
 
@@ -6573,7 +6576,6 @@ static int ipv6_sysctl_rtcache_flush(const struct ctl_table *ctl, int write,
 	if (ret)
 		return ret;
 
-	net = (struct net *)ctl->extra1;
 	delay = READ_ONCE(net->ipv6.sysctl.flush_delay);
 	fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
 	return 0;
@@ -6656,8 +6658,8 @@ static const struct ctl_table ipv6_route_table_template[] = {
 		.maxlen		=	sizeof(u8),
 		.mode		=	0644,
 		.proc_handler	=	proc_dou8vec_minmax,
-		.extra1		=	SYSCTL_ZERO,
-		.extra2		=	SYSCTL_ONE,
+		.min		=	0,
+		.max		=	1,
 	},
 };
 
@@ -6673,7 +6675,6 @@ struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
 		table[0].data = &net->ipv6.sysctl.ip6_rt_max_size;
 		table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
 		table[2].data = &net->ipv6.sysctl.flush_delay;
-		table[2].extra1 = net;
 		table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
 		table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
 		table[5].data = &net->ipv6.sysctl.ip6_rt_gc_interval;
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index ad4ad6f2001a..b8df02173d7a 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -37,9 +37,6 @@
 
 #define MPLS_NEIGH_TABLE_UNSPEC (NEIGH_LINK_TABLE + 1)
 
-static int label_limit = (1 << 20) - 1;
-static int ttl_max = 255;
-
 #if IS_ENABLED(CONFIG_NET_IP_TUNNEL)
 static size_t ipgre_mpls_encap_hlen(struct ip_tunnel_encap *e)
 {
@@ -1397,21 +1394,15 @@ static int mpls_netconf_dump_devconf(struct sk_buff *skb,
 static int mpls_conf_proc(const struct ctl_table *ctl, int write,
 			  void *buffer, size_t *lenp, loff_t *ppos)
 {
-	int oval = *(int *)ctl->data;
+	int *valp = ctl->data;
+	struct mpls_dev *mdev = container_of(valp, struct mpls_dev,
+					   input_enabled);
+	int oval = *valp;
 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
-	if (write) {
-		struct mpls_dev *mdev = ctl->extra1;
-		int i = (int *)ctl->data - (int *)mdev;
-		struct net *net = ctl->extra2;
-		int val = *(int *)ctl->data;
-
-		if (i == offsetof(struct mpls_dev, input_enabled) &&
-		    val != oval) {
-			mpls_netconf_notify_devconf(net, RTM_NEWNETCONF,
-						    NETCONFA_INPUT, mdev);
-		}
-	}
+	if (write && *valp != oval)
+		mpls_netconf_notify_devconf(dev_net(mdev->dev), RTM_NEWNETCONF,
+					    NETCONFA_INPUT, mdev);
 
 	return ret;
 }
@@ -1442,11 +1433,8 @@ static int mpls_dev_sysctl_register(struct net_device *dev,
 	/* Table data contains only offsets relative to the base of
 	 * the mdev at this point, so make them absolute.
 	 */
-	for (i = 0; i < table_size; i++) {
+	for (i = 0; i < table_size; i++)
 		table[i].data = (char *)mdev + (uintptr_t)table[i].data;
-		table[i].extra1 = mdev;
-		table[i].extra2 = net;
-	}
 
 	snprintf(path, sizeof(path), "net/mpls/conf/%s", dev->name);
 
@@ -2694,15 +2682,10 @@ static int mpls_platform_labels(const struct ctl_table *table, int write,
 	struct net *net = table->data;
 	int platform_labels = net->mpls.platform_labels;
 	int ret;
-	struct ctl_table tmp = {
-		.procname	= table->procname,
-		.data		= &platform_labels,
-		.maxlen		= sizeof(int),
-		.mode		= table->mode,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= &label_limit,
-	};
+	struct ctl_table tmp = *table;
 
+	tmp.data = &platform_labels;
+	tmp.maxlen = sizeof(platform_labels);
 	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
 
 	if (write && ret == 0)
@@ -2721,6 +2704,8 @@ static const struct ctl_table mpls_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= mpls_platform_labels,
+		.min		= 0,
+		.max		= (1 << 20) - 1,
 	},
 	{
 		.procname	= "ip_ttl_propagate",
@@ -2728,8 +2713,8 @@ static const struct ctl_table mpls_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "default_ttl",
@@ -2737,8 +2722,8 @@ static const struct ctl_table mpls_table[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ONE,
-		.extra2		= &ttl_max,
+		.min		= 1,
+		.max		= 255,
 	},
 };
 
-- 
2.25.1


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

* [RFC PATCH 31/39] ipvs: derive sysctl ownership from numeric storage
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (29 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 30/39] net: resolve sysctl owners from their backing data wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 32/39] sysctl: remove obsolete nr_open bound variables wen.yang
                   ` (7 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Derive IPVS ownership from each sysctl field instead of extra pointers.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/netfilter/ipvs/ip_vs_ctl.c | 156 +++++++++++++++++++--------------
 1 file changed, 88 insertions(+), 68 deletions(-)

diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 6c29a901d139..ed8e641da2ae 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -16,7 +16,6 @@
 #define pr_fmt(fmt) "IPVS: " fmt
 
 #include <linux/module.h>
-#include <linux/limits.h>
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/capability.h>
@@ -2375,6 +2374,8 @@ proc_do_conn_max(const struct ctl_table *table, int write,
 		 void *buffer, size_t *lenp, loff_t *ppos)
 {
 	int *valp = table->data;
+	struct netns_ipvs *ipvs = container_of(valp, struct netns_ipvs,
+					    sysctl_conn_max);
 	/* We can not use *valp to check if new value is provided, use INT_MIN
 	 * for this because different admins change different limits.
 	 */
@@ -2382,14 +2383,13 @@ proc_do_conn_max(const struct ctl_table *table, int write,
 	int val = write ? unset : READ_ONCE(*valp);
 	int rc;
 
-	const struct ctl_table tmp = {
-		.data = &val,
-		.maxlen = sizeof(int),
-	};
+	struct ctl_table tmp = *table;
+
+	tmp.data = &val;
+	tmp.maxlen = sizeof(val);
 
 	rc = proc_dointvec(&tmp, write, buffer, lenp, ppos);
 	if (write && !rc && val != unset) {
-		struct netns_ipvs *ipvs = table->extra2;
 		bool priv = capable(CAP_NET_ADMIN);
 		int max;
 
@@ -2410,19 +2410,17 @@ proc_do_conn_max(const struct ctl_table *table, int write,
 }
 
 static int
-proc_do_defense_mode(const struct ctl_table *table, int write,
-		     void *buffer, size_t *lenp, loff_t *ppos)
+proc_do_defense_mode(struct netns_ipvs *ipvs, const struct ctl_table *table,
+		     int write, void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct netns_ipvs *ipvs = table->extra2;
 	int *valp = table->data;
 	int val = *valp;
 	int rc;
 
-	struct ctl_table tmp = {
-		.data = &val,
-		.maxlen = sizeof(int),
-		.mode = table->mode,
-	};
+	struct ctl_table tmp = *table;
+
+	tmp.data = &val;
+	tmp.maxlen = sizeof(val);
 
 	rc = proc_dointvec(&tmp, write, buffer, lenp, ppos);
 	if (write && (*valp != val)) {
@@ -2436,19 +2434,49 @@ proc_do_defense_mode(const struct ctl_table *table, int write,
 	return rc;
 }
 
+static int proc_do_drop_entry(const struct ctl_table *table, int write,
+			      void *buffer, size_t *lenp, loff_t *ppos)
+{
+	int *valp = table->data;
+	struct netns_ipvs *ipvs = container_of(valp, struct netns_ipvs,
+					    sysctl_drop_entry);
+
+	return proc_do_defense_mode(ipvs, table, write, buffer, lenp, ppos);
+}
+
+static int proc_do_drop_packet(const struct ctl_table *table, int write,
+			       void *buffer, size_t *lenp, loff_t *ppos)
+{
+	int *valp = table->data;
+	struct netns_ipvs *ipvs = container_of(valp, struct netns_ipvs,
+					    sysctl_drop_packet);
+
+	return proc_do_defense_mode(ipvs, table, write, buffer, lenp, ppos);
+}
+
+static int proc_do_secure_tcp(const struct ctl_table *table, int write,
+			      void *buffer, size_t *lenp, loff_t *ppos)
+{
+	int *valp = table->data;
+	struct netns_ipvs *ipvs = container_of(valp, struct netns_ipvs,
+					    sysctl_secure_tcp);
+
+	return proc_do_defense_mode(ipvs, table, write, buffer, lenp, ppos);
+}
+
 static int
 proc_do_sync_threshold(const struct ctl_table *table, int write,
 		       void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct netns_ipvs *ipvs = table->extra2;
-	int *valp = table->data;
+	int (*valp)[2] = table->data;
+	struct netns_ipvs *ipvs = container_of(valp, struct netns_ipvs,
+					    sysctl_sync_threshold);
 	int val[2];
 	int rc;
-	struct ctl_table tmp = {
-		.data = &val,
-		.maxlen = table->maxlen,
-		.mode = table->mode,
-	};
+	struct ctl_table tmp = *table;
+
+	tmp.data = val;
+	tmp.maxlen = sizeof(val);
 
 	mutex_lock(&ipvs->sync_mutex);
 	memcpy(val, valp, sizeof(val));
@@ -2475,11 +2503,10 @@ proc_do_sync_ports(const struct ctl_table *table, int write,
 	int val = *valp;
 	int rc;
 
-	struct ctl_table tmp = {
-		.data = &val,
-		.maxlen = sizeof(int),
-		.mode = table->mode,
-	};
+	struct ctl_table tmp = *table;
+
+	tmp.data = &val;
+	tmp.maxlen = sizeof(val);
 
 	rc = proc_dointvec(&tmp, write, buffer, lenp, ppos);
 	if (write && (*valp != val)) {
@@ -2494,8 +2521,9 @@ proc_do_sync_ports(const struct ctl_table *table, int write,
 static int ipvs_proc_est_cpumask_set(const struct ctl_table *table,
 				     void *buffer)
 {
-	struct netns_ipvs *ipvs = table->extra2;
 	cpumask_var_t *valp = table->data;
+	struct netns_ipvs *ipvs = container_of(valp, struct netns_ipvs,
+					    sysctl_est_cpulist);
 	cpumask_var_t newmask;
 	int ret;
 
@@ -2533,8 +2561,9 @@ static int ipvs_proc_est_cpumask_set(const struct ctl_table *table,
 static int ipvs_proc_est_cpumask_get(const struct ctl_table *table,
 				     void *buffer, size_t size)
 {
-	struct netns_ipvs *ipvs = table->extra2;
 	cpumask_var_t *valp = table->data;
+	struct netns_ipvs *ipvs = container_of(valp, struct netns_ipvs,
+					    sysctl_est_cpulist);
 	struct cpumask *mask;
 	int ret;
 
@@ -2584,16 +2613,16 @@ static int ipvs_proc_est_cpulist(const struct ctl_table *table, int write,
 static int ipvs_proc_est_nice(const struct ctl_table *table, int write,
 			      void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct netns_ipvs *ipvs = table->extra2;
 	int *valp = table->data;
+	struct netns_ipvs *ipvs = container_of(valp, struct netns_ipvs,
+					    sysctl_est_nice);
 	int val = *valp;
 	int ret;
 
-	struct ctl_table tmp_table = {
-		.data = &val,
-		.maxlen = sizeof(int),
-		.mode = table->mode,
-	};
+	struct ctl_table tmp_table = *table;
+
+	tmp_table.data = &val;
+	tmp_table.maxlen = sizeof(val);
 
 	ret = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
 	if (write && ret >= 0) {
@@ -2614,16 +2643,16 @@ static int ipvs_proc_est_nice(const struct ctl_table *table, int write,
 static int ipvs_proc_run_estimation(const struct ctl_table *table, int write,
 				    void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct netns_ipvs *ipvs = table->extra2;
 	int *valp = table->data;
+	struct netns_ipvs *ipvs = container_of(valp, struct netns_ipvs,
+					    sysctl_run_estimation);
 	int val = *valp;
 	int ret;
 
-	struct ctl_table tmp_table = {
-		.data = &val,
-		.maxlen = sizeof(int),
-		.mode = table->mode,
-	};
+	struct ctl_table tmp_table = *table;
+
+	tmp_table.data = &val;
+	tmp_table.maxlen = sizeof(val);
 
 	ret = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
 	if (write && ret >= 0) {
@@ -2640,15 +2669,16 @@ static int ipvs_proc_run_estimation(const struct ctl_table *table, int write,
 static int ipvs_proc_conn_lfactor(const struct ctl_table *table, int write,
 				  void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct netns_ipvs *ipvs = table->extra2;
 	int *valp = table->data;
+	struct netns_ipvs *ipvs = container_of(valp, struct netns_ipvs,
+					    sysctl_conn_lfactor);
 	int val = *valp;
 	int ret;
 
-	struct ctl_table tmp_table = {
-		.data = &val,
-		.maxlen = sizeof(int),
-	};
+	struct ctl_table tmp_table = *table;
+
+	tmp_table.data = &val;
+	tmp_table.maxlen = sizeof(val);
 
 	ret = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
 	if (write && ret >= 0) {
@@ -2667,15 +2697,16 @@ static int ipvs_proc_conn_lfactor(const struct ctl_table *table, int write,
 static int ipvs_proc_svc_lfactor(const struct ctl_table *table, int write,
 				 void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct netns_ipvs *ipvs = table->extra2;
 	int *valp = table->data;
+	struct netns_ipvs *ipvs = container_of(valp, struct netns_ipvs,
+					    sysctl_svc_lfactor);
 	int val = *valp;
 	int ret;
 
-	struct ctl_table tmp_table = {
-		.data = &val,
-		.maxlen = sizeof(int),
-	};
+	struct ctl_table tmp_table = *table;
+
+	tmp_table.data = &val;
+	tmp_table.maxlen = sizeof(val);
 
 	ret = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
 	if (write && ret >= 0) {
@@ -2726,13 +2757,13 @@ static struct ctl_table vs_vars[] = {
 		.procname	= "drop_entry",
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_do_defense_mode,
+		.proc_handler	= proc_do_drop_entry,
 	},
 	{
 		.procname	= "drop_packet",
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_do_defense_mode,
+		.proc_handler	= proc_do_drop_packet,
 	},
 #ifdef CONFIG_IP_VS_NFCT
 	{
@@ -2746,7 +2777,7 @@ static struct ctl_table vs_vars[] = {
 		.procname	= "secure_tcp",
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
-		.proc_handler	= proc_do_defense_mode,
+		.proc_handler	= proc_do_secure_tcp,
 	},
 	{
 		.procname	= "snat_reroute",
@@ -2759,8 +2790,8 @@ static struct ctl_table vs_vars[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "sync_ports",
@@ -2836,8 +2867,8 @@ static struct ctl_table vs_vars[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_THREE,
+		.min		= 0,
+		.max		= 3,
 	},
 	{
 		.procname	= "nat_icmp_send",
@@ -5071,10 +5102,6 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs)
 	} else
 		tbl = vs_vars;
 	/* Initialize sysctl defaults */
-	for (idx = 0; idx < ARRAY_SIZE(vs_vars); idx++) {
-		if (tbl[idx].proc_handler == proc_do_defense_mode)
-			tbl[idx].extra2 = ipvs;
-	}
 	idx = 0;
 	ipvs->sysctl_amemthresh = 1024;
 	tbl[idx++].data = &ipvs->sysctl_amemthresh;
@@ -5085,7 +5112,6 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs)
 	ipvs->conn_max_limit = net_eq(net, &init_net) ? IP_VS_CONN_MAX :
 			       READ_ONCE(*(int *)vs_vars[idx].data);
 	ipvs->sysctl_conn_max = ipvs->conn_max_limit;
-	tbl[idx].extra2 = ipvs;
 	tbl[idx++].data = &ipvs->sysctl_conn_max;
 
 	tbl[idx++].data = &ipvs->sysctl_drop_entry;
@@ -5120,7 +5146,6 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs)
 	ipvs->sysctl_sync_threshold[0] = DEFAULT_SYNC_THRESHOLD;
 	ipvs->sysctl_sync_threshold[1] = DEFAULT_SYNC_PERIOD;
 	tbl[idx].data = &ipvs->sysctl_sync_threshold;
-	tbl[idx].extra2 = ipvs;
 	tbl[idx++].maxlen = sizeof(ipvs->sysctl_sync_threshold);
 	ipvs->sysctl_sync_refresh_period = DEFAULT_SYNC_REFRESH_PERIOD;
 	tbl[idx++].data = &ipvs->sysctl_sync_refresh_period;
@@ -5138,29 +5163,24 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs)
 	ipvs->sysctl_run_estimation = 1;
 	if (unpriv)
 		tbl[idx].mode = 0444;
-	tbl[idx].extra2 = ipvs;
 	tbl[idx++].data = &ipvs->sysctl_run_estimation;
 
 	ipvs->est_cpulist_valid = 0;
 	if (unpriv)
 		tbl[idx].mode = 0444;
-	tbl[idx].extra2 = ipvs;
 	tbl[idx++].data = &ipvs->sysctl_est_cpulist;
 
 	ipvs->sysctl_est_nice = IPVS_EST_NICE;
 	if (unpriv)
 		tbl[idx].mode = 0444;
-	tbl[idx].extra2 = ipvs;
 	tbl[idx++].data = &ipvs->sysctl_est_nice;
 
 	if (unpriv)
 		tbl[idx].mode = 0444;
-	tbl[idx].extra2 = ipvs;
 	tbl[idx++].data = &ipvs->sysctl_conn_lfactor;
 
 	if (unpriv)
 		tbl[idx].mode = 0444;
-	tbl[idx].extra2 = ipvs;
 	tbl[idx++].data = &ipvs->sysctl_svc_lfactor;
 
 #ifdef CONFIG_IP_VS_DEBUG
-- 
2.25.1


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

* [RFC PATCH 32/39] sysctl: remove obsolete nr_open bound variables
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (30 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 31/39] ipvs: derive sysctl ownership from numeric storage wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 33/39] ipv6: move address sysctl context to priv wen.yang
                   ` (6 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Remove nr_open bound variables and declarations now that limits are inline.

Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 fs/file.c            | 5 -----
 include/linux/file.h | 2 --
 2 files changed, 7 deletions(-)

diff --git a/fs/file.c b/fs/file.c
index 4cd40dfa6129..25fc2de96e9b 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -94,11 +94,6 @@ bool __file_ref_put(file_ref_t *ref, unsigned long cnt)
 EXPORT_SYMBOL_GPL(__file_ref_put);
 
 unsigned int sysctl_nr_open __read_mostly = 1024*1024;
-unsigned int sysctl_nr_open_min = BITS_PER_LONG;
-/* our min() is unusable in constant expressions ;-/ */
-#define __const_min(x, y) ((x) < (y) ? (x) : (y))
-unsigned int sysctl_nr_open_max =
-	__const_min(INT_MAX, ~(size_t)0/sizeof(void *)) & -BITS_PER_LONG;
 
 static void __free_fdtable(struct fdtable *fdt)
 {
diff --git a/include/linux/file.h b/include/linux/file.h
index 41c3c0be1064..634f1770e9a6 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
@@ -126,8 +126,6 @@ int receive_fd_replace(int new_fd, struct file *file, unsigned int o_flags);
 extern void flush_delayed_fput(void);
 extern void __fput_sync(struct file *);
 
-extern unsigned int sysctl_nr_open_min, sysctl_nr_open_max;
-
 /*
  * fd_prepare: Combined fd + file allocation cleanup class.
  * @fd: Allocated fd
-- 
2.25.1


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

* [RFC PATCH 33/39] ipv6: move address sysctl context to priv
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (31 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 32/39] sysctl: remove obsolete nr_open bound variables wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 34/39] ipv4: move device " wen.yang
                   ` (5 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

addrconf's handlers need the inet6_dev and the netns to find the devconf
field and to notify after a change. The table carries them in extra1 and
extra2, next to the numeric bounds in the same two fields, so every
handler has to know which meaning applies.

Carry the pair in priv instead, leave extra1/extra2 to the bounds, and
let the handlers use data as the devconf field directly. Registration
only fills priv, so it can no longer overwrite a value pointer or a
range.

Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/ipv6/addrconf.c | 163 +++++++++++++++++++++++++++-----------------
 1 file changed, 99 insertions(+), 64 deletions(-)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index c8c0dd04982d..27eaa95f1f21 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -806,6 +806,12 @@ static int inet6_netconf_dump_devconf(struct sk_buff *skb,
 }
 
 #ifdef CONFIG_SYSCTL
+/* Owned by the sysctl allocation; idev (if any) and net outlive it. */
+struct addrconf_sysctl_ctx {
+	struct inet6_dev *idev;
+	struct net *net;
+};
+
 static void dev_forward_change(struct inet6_dev *idev)
 {
 	struct net_device *dev;
@@ -873,9 +879,10 @@ static void addrconf_forward_change(struct net *net, __s32 newf)
 	}
 }
 
-static int addrconf_fixup_forwarding(const struct ctl_table *table, int *p, int newf)
+static int addrconf_fixup_forwarding(const struct addrconf_sysctl_ctx *ctx,
+				     int *p, int newf)
 {
-	struct net *net = (struct net *)table->extra2;
+	struct net *net = ctx->net;
 	int old;
 
 	if (!rtnl_net_trylock(net))
@@ -911,7 +918,7 @@ static int addrconf_fixup_forwarding(const struct ctl_table *table, int *p, int
 						     NETCONFA_IFINDEX_ALL,
 						     net->ipv6.devconf_all);
 	} else if ((!newf) ^ (!old))
-		dev_forward_change((struct inet6_dev *)table->extra1);
+		dev_forward_change(ctx->idev);
 	rtnl_net_unlock(net);
 
 	if (newf)
@@ -940,9 +947,10 @@ static void addrconf_linkdown_change(struct net *net, __s32 newf)
 	}
 }
 
-static int addrconf_fixup_linkdown(const struct ctl_table *table, int *p, int newf)
+static int addrconf_fixup_linkdown(const struct addrconf_sysctl_ctx *ctx,
+				   int *p, int newf)
 {
-	struct net *net = (struct net *)table->extra2;
+	struct net *net = ctx->net;
 	int old;
 
 	if (!rtnl_net_trylock(net))
@@ -969,7 +977,7 @@ static int addrconf_fixup_linkdown(const struct ctl_table *table, int *p, int ne
 						     net->ipv6.devconf_all);
 	} else {
 		if (!newf ^ !old) {
-			struct inet6_dev *idev = table->extra1;
+			struct inet6_dev *idev = ctx->idev;
 
 			inet6_netconf_notify_devconf(net,
 						     RTM_NEWNETCONF,
@@ -6389,6 +6397,7 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 static int addrconf_sysctl_forward(const struct ctl_table *ctl, int write,
 		void *buffer, size_t *lenp, loff_t *ppos)
 {
+	const struct addrconf_sysctl_ctx *ctx = ctl->priv;
 	struct ctl_table lctl;
 	int *valp = ctl->data;
 	int val = *valp;
@@ -6406,20 +6415,20 @@ static int addrconf_sysctl_forward(const struct ctl_table *ctl, int write,
 		return ret;
 
 	if (write)
-		ret = addrconf_fixup_forwarding(ctl, valp, val);
+		ret = addrconf_fixup_forwarding(ctx, valp, val);
 	return ret;
 }
 
 static int addrconf_sysctl_mtu(const struct ctl_table *ctl, int write,
 		void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct inet6_dev *idev = ctl->extra1;
-	int min_mtu = IPV6_MIN_MTU;
+	const struct addrconf_sysctl_ctx *ctx = ctl->priv;
+	struct inet6_dev *idev = ctx->idev;
 	struct ctl_table lctl;
 
 	lctl = *ctl;
-	lctl.extra1 = &min_mtu;
-	lctl.extra2 = idev ? &idev->dev->mtu : NULL;
+	lctl.min = IPV6_MIN_MTU;
+	lctl.max = idev ? READ_ONCE(idev->dev->mtu) : INT_MAX;
 
 	return proc_dointvec_minmax(&lctl, write, buffer, lenp, ppos);
 }
@@ -6455,9 +6464,10 @@ static void addrconf_disable_change(struct net *net, __s32 newf)
 	}
 }
 
-static int addrconf_disable_ipv6(const struct ctl_table *table, int *p, int newf)
+static int addrconf_disable_ipv6(const struct addrconf_sysctl_ctx *ctx,
+				 int *p, int newf)
 {
-	struct net *net = (struct net *)table->extra2;
+	struct net *net = ctx->net;
 	int old;
 
 	if (p == &net->ipv6.devconf_dflt->disable_ipv6) {
@@ -6475,7 +6485,7 @@ static int addrconf_disable_ipv6(const struct ctl_table *table, int *p, int newf
 		WRITE_ONCE(net->ipv6.devconf_dflt->disable_ipv6, newf);
 		addrconf_disable_change(net, newf);
 	} else if ((!newf) ^ (!old)) {
-		dev_disable_change((struct inet6_dev *)table->extra1);
+		dev_disable_change(ctx->idev);
 	}
 
 	rtnl_net_unlock(net);
@@ -6485,6 +6495,7 @@ static int addrconf_disable_ipv6(const struct ctl_table *table, int *p, int newf
 static int addrconf_sysctl_disable(const struct ctl_table *ctl, int write,
 		void *buffer, size_t *lenp, loff_t *ppos)
 {
+	const struct addrconf_sysctl_ctx *ctx = ctl->priv;
 	struct ctl_table lctl;
 	int *valp = ctl->data;
 	int val = *valp;
@@ -6502,14 +6513,15 @@ static int addrconf_sysctl_disable(const struct ctl_table *ctl, int write,
 		return ret;
 
 	if (write)
-		ret = addrconf_disable_ipv6(ctl, valp, val);
+		ret = addrconf_disable_ipv6(ctx, valp, val);
 	return ret;
 }
 
 static int addrconf_sysctl_proxy_ndp(const struct ctl_table *ctl, int write,
 		void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct net *net = ctl->extra2;
+	const struct addrconf_sysctl_ctx *ctx = ctl->priv;
+	struct net *net = ctx->net;
 	int *valp = ctl->data;
 	int old, new;
 	int ret;
@@ -6533,7 +6545,7 @@ static int addrconf_sysctl_proxy_ndp(const struct ctl_table *ctl, int write,
 						     NETCONFA_IFINDEX_ALL,
 						     net->ipv6.devconf_all);
 		} else {
-			struct inet6_dev *idev = ctl->extra1;
+			struct inet6_dev *idev = ctx->idev;
 
 			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
 						     NETCONFA_PROXY_NEIGH,
@@ -6551,10 +6563,11 @@ static int addrconf_sysctl_addr_gen_mode(const struct ctl_table *ctl, int write,
 					 void *buffer, size_t *lenp,
 					 loff_t *ppos)
 {
+	const struct addrconf_sysctl_ctx *ctx = ctl->priv;
 	int ret = 0;
 	u32 new_val;
-	struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1;
-	struct net *net = (struct net *)ctl->extra2;
+	struct inet6_dev *idev = ctx->idev;
+	struct net *net = ctx->net;
 	struct ctl_table tmp = {
 		.data = &new_val,
 		.maxlen = sizeof(new_val),
@@ -6618,16 +6631,18 @@ static int addrconf_sysctl_stable_secret(const struct ctl_table *ctl, int write,
 					 void *buffer, size_t *lenp,
 					 loff_t *ppos)
 {
+	const struct addrconf_sysctl_ctx *ctx = ctl->priv;
 	int err;
 	struct in6_addr addr;
 	char str[IPV6_MAX_STRLEN];
-	struct ctl_table lctl = *ctl;
-	struct net *net = ctl->extra2;
+	struct ctl_table lctl;
+	struct net *net = ctx->net;
 	struct ipv6_stable_secret *secret = ctl->data;
 
 	if (&net->ipv6.devconf_all->stable_secret == ctl->data)
 		return -EIO;
 
+	lctl = *ctl;
 	lctl.maxlen = IPV6_MAX_STRLEN;
 	lctl.data = str;
 
@@ -6669,7 +6684,7 @@ static int addrconf_sysctl_stable_secret(const struct ctl_table *ctl, int write,
 			}
 		}
 	} else {
-		struct inet6_dev *idev = ctl->extra1;
+		struct inet6_dev *idev = ctx->idev;
 
 		WRITE_ONCE(idev->cnf.addr_gen_mode,
 			   IN6_ADDR_GEN_MODE_STABLE_PRIVACY);
@@ -6687,6 +6702,7 @@ int addrconf_sysctl_ignore_routes_with_linkdown(const struct ctl_table *ctl,
 						size_t *lenp,
 						loff_t *ppos)
 {
+	const struct addrconf_sysctl_ctx *ctx = ctl->priv;
 	struct ctl_table lctl;
 	int *valp = ctl->data;
 	int val = *valp;
@@ -6703,7 +6719,7 @@ int addrconf_sysctl_ignore_routes_with_linkdown(const struct ctl_table *ctl,
 		return ret;
 
 	if (write)
-		ret = addrconf_fixup_linkdown(ctl, valp, val);
+		ret = addrconf_fixup_linkdown(ctx, valp, val);
 	return ret;
 }
 
@@ -6749,9 +6765,10 @@ void addrconf_disable_policy_idev(struct inet6_dev *idev, int val)
 }
 
 static
-int addrconf_disable_policy(const struct ctl_table *ctl, int *valp, int val)
+int addrconf_disable_policy(const struct addrconf_sysctl_ctx *ctx,
+			    int *valp, int val)
 {
-	struct net *net = (struct net *)ctl->extra2;
+	struct net *net = ctx->net;
 	struct inet6_dev *idev;
 
 	if (valp == &net->ipv6.devconf_dflt->disable_policy) {
@@ -6773,7 +6790,7 @@ int addrconf_disable_policy(const struct ctl_table *ctl, int *valp, int val)
 				addrconf_disable_policy_idev(idev, val);
 		}
 	} else {
-		idev = (struct inet6_dev *)ctl->extra1;
+		idev = ctx->idev;
 		addrconf_disable_policy_idev(idev, val);
 	}
 
@@ -6784,6 +6801,7 @@ int addrconf_disable_policy(const struct ctl_table *ctl, int *valp, int val)
 static int addrconf_sysctl_disable_policy(const struct ctl_table *ctl, int write,
 				   void *buffer, size_t *lenp, loff_t *ppos)
 {
+	const struct addrconf_sysctl_ctx *ctx = ctl->priv;
 	struct ctl_table lctl;
 	int *valp = ctl->data;
 	int val = *valp;
@@ -6796,7 +6814,7 @@ static int addrconf_sysctl_disable_policy(const struct ctl_table *ctl, int write
 		return ret;
 
 	if (write && (*valp != val))
-		ret = addrconf_disable_policy(ctl, valp, val);
+		ret = addrconf_disable_policy(ctx, valp, val);
 
 	return ret;
 }
@@ -6823,16 +6841,17 @@ static void addrconf_force_forward_change(struct net *net, __s32 newf)
 static int addrconf_sysctl_force_forwarding(const struct ctl_table *ctl, int write,
 					    void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct inet6_dev *idev = ctl->extra1;
+	const struct addrconf_sysctl_ctx *ctx = ctl->priv;
+	struct inet6_dev *idev = ctx->idev;
 	struct ctl_table tmp_ctl = *ctl;
-	struct net *net = ctl->extra2;
+	struct net *net = ctx->net;
 	int *valp = ctl->data;
 	int new_val = *valp;
 	int old_val = *valp;
 	int ret;
 
-	tmp_ctl.extra1 = SYSCTL_ZERO;
-	tmp_ctl.extra2 = SYSCTL_ONE;
+	tmp_ctl.min = 0;
+	tmp_ctl.max = 1;
 	tmp_ctl.data = &new_val;
 
 	ret = proc_douintvec_minmax(&tmp_ctl, write, buffer, lenp, ppos);
@@ -6867,10 +6886,6 @@ static int addrconf_sysctl_force_forwarding(const struct ctl_table *ctl, int wri
 	return ret;
 }
 
-static int minus_one = -1;
-static const int two_five_five = 255;
-static u32 ioam6_if_id_max = U16_MAX;
-
 static const struct ctl_table addrconf_sysctl[] = {
 	{
 		.procname	= "forwarding",
@@ -6885,8 +6900,8 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= (void *)SYSCTL_ONE,
-		.extra2		= (void *)&two_five_five,
+		.min		= 1,
+		.max		= 255,
 	},
 	{
 		.procname	= "mtu",
@@ -6929,7 +6944,8 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= &minus_one,
+		.min		= (unsigned long)(long)-1,
+		.max		= INT_MAX,
 	},
 	{
 		.procname	= "router_solicitation_interval",
@@ -7037,7 +7053,8 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.maxlen		= sizeof(u32),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= (void *)SYSCTL_ONE,
+		.min		= 1,
+		.max		= UINT_MAX,
 	},
 	{
 		.procname	= "accept_ra_min_hop_limit",
@@ -7066,8 +7083,8 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "ra_honor_pio_pflag",
@@ -7075,8 +7092,8 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 #ifdef CONFIG_IPV6_ROUTER_PREF
 	{
@@ -7284,8 +7301,8 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= (void *)SYSCTL_ZERO,
-		.extra2		= (void *)&two_five_five,
+		.min		= 0,
+		.max		= 255,
 	},
 	{
 		.procname	= "rpl_seg_enabled",
@@ -7293,8 +7310,8 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler   = proc_dointvec_minmax,
-		.extra1         = SYSCTL_ZERO,
-		.extra2         = SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "ioam6_enabled",
@@ -7302,8 +7319,8 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= (void *)SYSCTL_ZERO,
-		.extra2		= (void *)SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "ioam6_id",
@@ -7311,8 +7328,8 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.maxlen		= sizeof(u32),
 		.mode		= 0644,
 		.proc_handler	= proc_douintvec_minmax,
-		.extra1		= (void *)SYSCTL_ZERO,
-		.extra2		= (void *)&ioam6_if_id_max,
+		.min		= 0,
+		.max		= U16_MAX,
 	},
 	{
 		.procname	= "ioam6_id_wide",
@@ -7327,8 +7344,8 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.maxlen		= sizeof(u8),
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
-		.extra1		= (void *)SYSCTL_ZERO,
-		.extra2		= (void *)SYSCTL_ONE,
+		.min		= 0,
+		.max		= 1,
 	},
 	{
 		.procname	= "accept_untracked_na",
@@ -7336,8 +7353,8 @@ static const struct ctl_table addrconf_sysctl[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec_minmax,
-		.extra1		= SYSCTL_ZERO,
-		.extra2		= SYSCTL_TWO,
+		.min		= 0,
+		.max		= 2,
 	},
 	{
 		.procname	= "force_forwarding",
@@ -7348,27 +7365,45 @@ static const struct ctl_table addrconf_sysctl[] = {
 	},
 };
 
+struct addrconf_sysctl_table {
+	struct ctl_table table[ARRAY_SIZE(addrconf_sysctl)];
+	struct addrconf_sysctl_ctx contexts[ARRAY_SIZE(addrconf_sysctl)];
+};
+
+static bool addrconf_sysctl_uses_context(proc_handler *handler)
+{
+	return handler == addrconf_sysctl_forward ||
+	       handler == addrconf_sysctl_mtu ||
+	       handler == addrconf_sysctl_disable ||
+	       handler == addrconf_sysctl_proxy_ndp ||
+	       handler == addrconf_sysctl_addr_gen_mode ||
+	       handler == addrconf_sysctl_stable_secret ||
+	       handler == addrconf_sysctl_ignore_routes_with_linkdown ||
+	       handler == addrconf_sysctl_disable_policy ||
+	       handler == addrconf_sysctl_force_forwarding;
+}
+
 static int __addrconf_sysctl_register(struct net *net, char *dev_name,
 		struct inet6_dev *idev, struct ipv6_devconf *p)
 {
 	size_t table_size = ARRAY_SIZE(addrconf_sysctl);
 	int i, ifindex;
+	struct addrconf_sysctl_table *t;
 	struct ctl_table *table;
 	char path[sizeof("net/ipv6/conf/") + IFNAMSIZ];
 
-	table = kmemdup(addrconf_sysctl, sizeof(addrconf_sysctl), GFP_KERNEL_ACCOUNT);
-	if (!table)
+	t = kmalloc(sizeof(*t), GFP_KERNEL_ACCOUNT);
+	if (!t)
 		goto out;
 
+	table = t->table;
+	memcpy(table, addrconf_sysctl, sizeof(addrconf_sysctl));
 	for (i = 0; i < table_size; i++) {
 		table[i].data += (char *)p - (char *)&ipv6_devconf;
-		/* If one of these is already set, then it is not safe to
-		 * overwrite either of them: this makes proc_dointvec_minmax
-		 * usable.
-		 */
-		if (!table[i].extra1 && !table[i].extra2) {
-			table[i].extra1 = idev; /* embedded; no ref */
-			table[i].extra2 = net;
+		if (addrconf_sysctl_uses_context(table[i].proc_handler)) {
+			t->contexts[i].idev = idev;
+			t->contexts[i].net = net;
+			table[i].priv = &t->contexts[i];
 		}
 	}
 
-- 
2.25.1


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

* [RFC PATCH 34/39] ipv4: move device sysctl context to priv
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (32 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 33/39] ipv6: move address sysctl context to priv wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 35/39] netfilter: move logger " wen.yang
                   ` (4 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Like addrconf, devinet needs the ipv4_devconf and the netns in each
handler, and the per-net ip_forward entry needs the all-devconf. Those
pointers currently share extra1/extra2 with the numeric bounds.

Put them in priv, keep data as the devconf field, and let registration
set only priv. The forwarding entry points data straight at the
all-devconf forwarding slot.

Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/ipv4/devinet.c | 53 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 37 insertions(+), 16 deletions(-)

diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 1e9d988849c9..e1899b268a34 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -2555,17 +2555,26 @@ static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
 	}
 }
 
+/* Owned by the registered table; devconf and net outlive its handlers. */
+struct devinet_sysctl_ctx {
+	struct ipv4_devconf *devconf;
+	struct net *net;
+};
+
 static int devinet_conf_proc(const struct ctl_table *ctl, int write,
 			     void *buffer, size_t *lenp, loff_t *ppos)
 {
-	int old_value = *(int *)ctl->data;
-	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
-	int new_value = *(int *)ctl->data;
+	const struct devinet_sysctl_ctx *ctx = ctl->priv;
+	int old_value, ret, new_value;
+
+	old_value = *(int *)ctl->data;
+	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+	new_value = *(int *)ctl->data;
 
 	if (write) {
-		struct ipv4_devconf *cnf = ctl->extra1;
+		struct ipv4_devconf *cnf = ctx->devconf;
 		int i = (int *)ctl->data - cnf->data;
-		struct net *net = ctl->extra2;
+		struct net *net = ctx->net;
 		int ifindex;
 
 		/* These attributes are bypassing the tracking state,
@@ -2597,7 +2606,8 @@ static int devinet_conf_proc(const struct ctl_table *ctl, int write,
 static int devinet_sysctl_forward(const struct ctl_table *ctl, int write,
 				  void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct net *net = ctl->extra2;
+	const struct devinet_sysctl_ctx *ctx = ctl->priv;
+	struct net *net = ctx->net;
 	int *valp = ctl->data;
 	int val = *valp;
 	int ret;
@@ -2617,7 +2627,7 @@ static int devinet_sysctl_forward(const struct ctl_table *ctl, int write,
 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
 				inet_forward_change(net);
 			} else {
-				struct ipv4_devconf *cnf = ctl->extra1;
+				struct ipv4_devconf *cnf = ctx->devconf;
 				struct in_device *idev =
 					container_of(cnf, struct in_device, cnf);
 				if (*valp)
@@ -2647,7 +2657,6 @@ static int devinet_sysctl_forward(const struct ctl_table *ctl, int write,
 		.maxlen		= sizeof(int), \
 		.mode		= mval, \
 		.proc_handler	= proc, \
-		.extra1		= &ipv4_devconf, \
 	}
 
 #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
@@ -2662,6 +2671,7 @@ static int devinet_sysctl_forward(const struct ctl_table *ctl, int write,
 static struct devinet_sysctl_table {
 	struct ctl_table_header *sysctl_header;
 	struct ctl_table devinet_vars[IPV4_DEVCONF_MAX];
+	struct devinet_sysctl_ctx contexts[IPV4_DEVCONF_MAX];
 } devinet_sysctl = {
 	.devinet_vars = {
 		DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
@@ -2725,8 +2735,12 @@ static int __devinet_sysctl_register(struct net *net, char *dev_name,
 
 	for (i = 0; i < ARRAY_SIZE(t->devinet_vars); i++) {
 		t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
-		t->devinet_vars[i].extra1 = p;
-		t->devinet_vars[i].extra2 = net;
+		if (t->devinet_vars[i].proc_handler == devinet_conf_proc ||
+		    t->devinet_vars[i].proc_handler == devinet_sysctl_forward) {
+			t->contexts[i].devconf = p;
+			t->contexts[i].net = net;
+			t->devinet_vars[i].priv = &t->contexts[i];
+		}
 	}
 
 	snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
@@ -2786,6 +2800,11 @@ static void devinet_sysctl_unregister(struct in_device *idev)
 	neigh_sysctl_unregister(idev->arp_parms);
 }
 
+struct devinet_forward_table {
+	struct ctl_table table[1];
+	struct devinet_sysctl_ctx context;
+};
+
 static const struct ctl_table ctl_forward_entry[] = {
 	{
 		.procname	= "ip_forward",
@@ -2794,8 +2813,6 @@ static const struct ctl_table ctl_forward_entry[] = {
 		.maxlen		= sizeof(int),
 		.mode		= 0644,
 		.proc_handler	= devinet_sysctl_forward,
-		.extra1		= &ipv4_devconf,
-		.extra2		= &init_net,
 	},
 };
 #endif
@@ -2805,6 +2822,7 @@ static __net_init int devinet_init_net(struct net *net)
 #ifdef CONFIG_SYSCTL
 	struct ctl_table_header *forw_hdr;
 	struct ctl_table *tbl;
+	struct devinet_forward_table *forward;
 #endif
 	struct ipv4_devconf *all, *dflt;
 	int err;
@@ -2825,13 +2843,16 @@ static __net_init int devinet_init_net(struct net *net)
 		goto err_alloc_dflt;
 
 #ifdef CONFIG_SYSCTL
-	tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL);
-	if (!tbl)
+	forward = kmalloc(sizeof(*forward), GFP_KERNEL);
+	if (!forward)
 		goto err_alloc_ctl;
 
+	tbl = forward->table;
+	memcpy(tbl, ctl_forward_entry, sizeof(ctl_forward_entry));
+	forward->context.devconf = all;
+	forward->context.net = net;
 	tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
-	tbl[0].extra1 = all;
-	tbl[0].extra2 = net;
+	tbl[0].priv = &forward->context;
 #endif
 
 	if (!net_eq(net, &init_net)) {
-- 
2.25.1


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

* [RFC PATCH 35/39] netfilter: move logger sysctl context to priv
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (33 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 34/39] ipv4: move device " wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 36/39] parport: move device " wen.yang
                   ` (3 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

nf_log keeps the netns in extra2 and encodes the protocol family into
extra1 as an integer cast to a pointer. Both share the fields that hold
numeric bounds elsewhere, and the family encoding is a type error
waiting to happen.

Carry {net, family} in priv, leave data NULL, and keep maxlen as the
proc_dostring() name length. The family becomes a real field.

Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 net/netfilter/nf_log.c | 67 ++++++++++++++++++++++++------------------
 1 file changed, 38 insertions(+), 29 deletions(-)

diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c
index f1d3649e3fc6..8163774ad4d4 100644
--- a/net/netfilter/nf_log.c
+++ b/net/netfilter/nf_log.c
@@ -420,8 +420,18 @@ static const struct seq_operations nflog_seq_ops = {
 #endif /* PROC_FS */
 
 #ifdef CONFIG_SYSCTL
+struct nf_log_sysctl_context {
+	struct net *net;
+	unsigned int family;
+};
+
+struct nf_log_sysctl_table {
+	struct ctl_table vars[NFPROTO_NUMPROTO];
+	struct nf_log_sysctl_context context[NFPROTO_NUMPROTO];
+};
+
 static char nf_log_sysctl_fnames[NFPROTO_NUMPROTO-NFPROTO_UNSPEC][3];
-static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO];
+static struct nf_log_sysctl_table nf_log_sysctl_table;
 static struct ctl_table_header *nf_log_sysctl_fhdr;
 
 static struct ctl_table nf_log_sysctl_ftable[] = {
@@ -440,8 +450,9 @@ static int nf_log_proc_dostring(const struct ctl_table *table, int write,
 	const struct nf_logger *logger;
 	char buf[NFLOGGER_NAME_LEN];
 	int r = 0;
-	int tindex = (unsigned long)table->extra1;
-	struct net *net = table->extra2;
+	const struct nf_log_sysctl_context *ctx = table->priv;
+	unsigned int tindex = ctx->family;
+	struct net *net = ctx->net;
 
 	if (write) {
 		struct ctl_table tmp = *table;
@@ -451,6 +462,7 @@ static int nf_log_proc_dostring(const struct ctl_table *table, int write,
 		 */
 		buf[0] = '\0';
 		tmp.data = buf;
+		tmp.maxlen = sizeof(buf);
 		r = proc_dostring(&tmp, write, buffer, lenp, ppos);
 		if (r)
 			return r;
@@ -471,6 +483,7 @@ static int nf_log_proc_dostring(const struct ctl_table *table, int write,
 		struct ctl_table tmp = *table;
 
 		tmp.data = buf;
+		tmp.maxlen = sizeof(buf);
 		mutex_lock(&nf_log_mutex);
 		logger = nft_log_dereference(net->nf.nf_loggers[tindex]);
 		if (!logger)
@@ -486,42 +499,38 @@ static int nf_log_proc_dostring(const struct ctl_table *table, int write,
 
 static int netfilter_log_sysctl_init(struct net *net)
 {
+	struct nf_log_sysctl_table *t = &nf_log_sysctl_table;
 	int i;
-	struct ctl_table *table;
 
-	table = nf_log_sysctl_table;
 	if (!net_eq(net, &init_net)) {
-		table = kmemdup(nf_log_sysctl_table,
-				 sizeof(nf_log_sysctl_table),
-				 GFP_KERNEL_ACCOUNT);
-		if (!table)
-			goto err_alloc;
+		t = kmemdup(t, sizeof(*t), GFP_KERNEL_ACCOUNT);
+		if (!t)
+			return -ENOMEM;
 	} else {
 		for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) {
-			snprintf(nf_log_sysctl_fnames[i],
-				 3, "%d", i);
-			nf_log_sysctl_table[i].procname	=
-				nf_log_sysctl_fnames[i];
-			nf_log_sysctl_table[i].maxlen = NFLOGGER_NAME_LEN;
-			nf_log_sysctl_table[i].mode = 0644;
-			nf_log_sysctl_table[i].proc_handler =
-				nf_log_proc_dostring;
-			nf_log_sysctl_table[i].extra1 =
-				(void *)(unsigned long) i;
+			snprintf(nf_log_sysctl_fnames[i], 3, "%d", i);
+			t->vars[i].procname = nf_log_sysctl_fnames[i];
+			t->vars[i].maxlen = NFLOGGER_NAME_LEN;
+			t->vars[i].mode = 0644;
+			t->vars[i].proc_handler = nf_log_proc_dostring;
 		}
 		nf_log_sysctl_fhdr = register_net_sysctl(net, "net/netfilter",
 							 nf_log_sysctl_ftable);
 		if (!nf_log_sysctl_fhdr)
-			goto err_freg;
+			return -ENOMEM;
 	}
 
-	for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
-		table[i].extra2 = net;
+	for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) {
+		t->context[i].net = net;
+		t->context[i].family = i;
+		t->vars[i].data = NULL;
+		t->vars[i].priv = &t->context[i];
+	}
 
 	net->nf.nf_log_dir_header = register_net_sysctl_sz(net,
 							   "net/netfilter/nf_log",
-							   table,
-							   ARRAY_SIZE(nf_log_sysctl_table));
+							   t->vars,
+							   ARRAY_SIZE(t->vars));
 	if (!net->nf.nf_log_dir_header)
 		goto err_reg;
 
@@ -529,22 +538,22 @@ static int netfilter_log_sysctl_init(struct net *net)
 
 err_reg:
 	if (!net_eq(net, &init_net))
-		kfree(table);
+		kfree(t);
 	else
 		unregister_net_sysctl_table(nf_log_sysctl_fhdr);
-err_freg:
-err_alloc:
 	return -ENOMEM;
 }
 
 static void netfilter_log_sysctl_exit(struct net *net)
 {
 	const struct ctl_table *table;
+	const struct nf_log_sysctl_table *t;
 
 	table = net->nf.nf_log_dir_header->ctl_table_arg;
+	t = container_of(table, struct nf_log_sysctl_table, vars[0]);
 	unregister_net_sysctl_table(net->nf.nf_log_dir_header);
 	if (!net_eq(net, &init_net))
-		kfree(table);
+		kfree(t);
 	else
 		unregister_net_sysctl_table(nf_log_sysctl_fhdr);
 }
-- 
2.25.1


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

* [RFC PATCH 36/39] parport: move device sysctl context to priv
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (34 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 35/39] netfilter: move logger " wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 37/39] net: move namespace " wen.yang
                   ` (2 subsequent siblings)
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

The per-port table uses extra1 for both the spintime minimum and the
port context of the active and hardware entries. During registration,
the loop also assigns the port pointer to vars[0].extra1, overwriting
the minimum. A spintime write can consequently be checked against data
from struct parport instead of the intended minimum.

Move the port context to priv and use numeric bounds for spintime.
Keep probe_info in data and set priv only for the entries that need the
port. Separating context from bounds prevents this overwrite.

The spintime bug exists in the base tree and can also be fixed separately;
this RFC does not depend on that fix.

Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 drivers/parport/procfs.c | 51 +++++++++++++++-------------------------
 1 file changed, 19 insertions(+), 32 deletions(-)

diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c
index 1bfafc2a5724..6f6d96c37a93 100644
--- a/drivers/parport/procfs.c
+++ b/drivers/parport/procfs.c
@@ -36,7 +36,7 @@
 static int do_active_device(const struct ctl_table *table, int write,
 		      void *result, size_t *lenp, loff_t *ppos)
 {
-	struct parport *port = (struct parport *)table->extra1;
+	struct parport *port = table->priv;
 	char buffer[256];
 	struct pardevice *dev;
 	int len = 0;
@@ -73,7 +73,7 @@ static int do_active_device(const struct ctl_table *table, int write,
 static int do_autoprobe(const struct ctl_table *table, int write,
 			void *result, size_t *lenp, loff_t *ppos)
 {
-	struct parport_device_info *info = table->extra2;
+	struct parport_device_info *info = table->data;
 	const char *str;
 	char buffer[256];
 	int len = 0;
@@ -116,7 +116,7 @@ static int do_autoprobe(const struct ctl_table *table, int write,
 static int do_hardware_base_addr(const struct ctl_table *table, int write,
 				 void *result, size_t *lenp, loff_t *ppos)
 {
-	struct parport *port = (struct parport *)table->extra1;
+	struct parport *port = table->priv;
 	char buffer[64];
 	int len = 0;
 
@@ -143,7 +143,7 @@ static int do_hardware_base_addr(const struct ctl_table *table, int write,
 static int do_hardware_irq(const struct ctl_table *table, int write,
 			   void *result, size_t *lenp, loff_t *ppos)
 {
-	struct parport *port = (struct parport *)table->extra1;
+	struct parport *port = table->priv;
 	char buffer[20];
 	int len = 0;
 
@@ -170,7 +170,7 @@ static int do_hardware_irq(const struct ctl_table *table, int write,
 static int do_hardware_dma(const struct ctl_table *table, int write,
 			   void *result, size_t *lenp, loff_t *ppos)
 {
-	struct parport *port = (struct parport *)table->extra1;
+	struct parport *port = table->priv;
 	char buffer[20];
 	int len = 0;
 
@@ -197,7 +197,7 @@ static int do_hardware_dma(const struct ctl_table *table, int write,
 static int do_hardware_modes(const struct ctl_table *table, int write,
 			     void *result, size_t *lenp, loff_t *ppos)
 {
-	struct parport *port = (struct parport *)table->extra1;
+	struct parport *port = table->priv;
 	char buffer[40];
 	int len = 0;
 
@@ -236,19 +236,6 @@ do {									\
 	return 0;
 }
 
-static const unsigned long parport_min_timeslice_value =
-PARPORT_MIN_TIMESLICE_VALUE;
-
-static const unsigned long parport_max_timeslice_value =
-PARPORT_MAX_TIMESLICE_VALUE;
-
-static const  int parport_min_spintime_value =
-PARPORT_MIN_SPINTIME_VALUE;
-
-static const int parport_max_spintime_value =
-PARPORT_MAX_SPINTIME_VALUE;
-
-
 struct parport_sysctl_table {
 	struct ctl_table_header *port_header;
 	struct ctl_table_header *devices_header;
@@ -270,8 +257,8 @@ static const struct parport_sysctl_table parport_sysctl_template = {
 			.maxlen		= sizeof(int),
 			.mode		= 0644,
 			.proc_handler	= proc_dointvec_minmax,
-			.extra1		= (void*) &parport_min_spintime_value,
-			.extra2		= (void*) &parport_max_spintime_value
+			.min		= PARPORT_MIN_SPINTIME_VALUE,
+			.max		= PARPORT_MAX_SPINTIME_VALUE,
 		},
 		{
 			.procname	= "base-addr",
@@ -367,8 +354,8 @@ parport_device_sysctl_template = {
 			.maxlen		= sizeof(unsigned long),
 			.mode		= 0644,
 			.proc_handler	= proc_doulongvec_ms_jiffies_minmax,
-			.extra1		= (void*) &parport_min_timeslice_value,
-			.extra2		= (void*) &parport_max_timeslice_value
+			.min		= PARPORT_MIN_TIMESLICE_VALUE,
+			.max		= PARPORT_MAX_TIMESLICE_VALUE,
 		},
 	},
 	{
@@ -397,8 +384,8 @@ parport_default_sysctl_table = {
 			.maxlen		= sizeof(parport_default_timeslice),
 			.mode		= 0644,
 			.proc_handler	= proc_doulongvec_ms_jiffies_minmax,
-			.extra1		= (void*) &parport_min_timeslice_value,
-			.extra2		= (void*) &parport_max_timeslice_value
+			.min		= PARPORT_MIN_TIMESLICE_VALUE,
+			.max		= PARPORT_MAX_TIMESLICE_VALUE,
 		},
 		{
 			.procname	= "spintime",
@@ -406,8 +393,8 @@ parport_default_sysctl_table = {
 			.maxlen		= sizeof(parport_default_spintime),
 			.mode		= 0644,
 			.proc_handler	= proc_dointvec_minmax,
-			.extra1		= (void*) &parport_min_spintime_value,
-			.extra2		= (void*) &parport_max_spintime_value
+			.min		= PARPORT_MIN_SPINTIME_VALUE,
+			.max		= PARPORT_MAX_SPINTIME_VALUE,
 		},
 	}
 };
@@ -422,15 +409,15 @@ int parport_proc_register(struct parport *port)
 	if (t == NULL)
 		return -ENOMEM;
 
-	t->device_dir[0].extra1 = port;
+	t->device_dir[0].priv = port;
 
 	t->vars[0].data = &port->spintime;
-	for (i = 0; i < 5; i++) {
-		t->vars[i].extra1 = port;
+	for (i = 1; i < 5; i++)
+		t->vars[i].priv = port;
 #ifdef CONFIG_PARPORT_1284
-		t->vars[5 + i].extra2 = &port->probe_info[i];
+	for (i = 0; i < 5; i++)
+		t->vars[5 + i].data = &port->probe_info[i];
 #endif /* IEEE 1284 support */
-	}
 
 	tmp_dir_path = kasprintf(GFP_KERNEL, "dev/parport/%s/devices", port->name);
 	if (!tmp_dir_path) {
-- 
2.25.1


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

* [RFC PATCH 37/39] net: move namespace sysctl context to priv
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (35 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 36/39] parport: move device " wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 38/39] arm64: move vector-length " wen.yang
  2026-10-28 16:35 ` [RFC PATCH 39/39] sysctl: remove the extra pointers and the compatibility bounds read wen.yang
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Several per-net handlers store the owning struct net in data and resolve
their real storage elsewhere: the route flush entry, MPLS platform
labels, the VRF strict-mode switch, the RPS default mask and the IPv4
local port range and multipath hash seed.

Point data at backed storage where one exists, otherwise leave it NULL,
and carry the namespace in priv. The duplication paths copy the
namespace into priv instead of offsetting data, so a handler can no
longer mistake an offset for its owner.

Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 drivers/net/vrf.c          |  4 ++--
 net/core/sysctl_net_core.c | 11 +++++++++--
 net/ipv4/route.c           |  4 ++--
 net/ipv4/sysctl_net_ipv4.c | 13 ++++++++-----
 net/mpls/af_mpls.c         |  9 ++++++---
 5 files changed, 27 insertions(+), 14 deletions(-)

diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 993cc2d120fd..fcbb43c72ff7 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -1826,7 +1826,7 @@ static int vrf_strict_mode_change(struct vrf_map *vmap, bool new_mode)
 static int vrf_shared_table_handler(const struct ctl_table *table, int write,
 				    void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct net *net = table->data;
+	struct net *net = table->priv;
 	struct vrf_map *vmap = netns_vrf_map(net);
 	int proc_strict_mode = 0;
 	struct ctl_table tmp = *table;
@@ -1865,7 +1865,7 @@ static int vrf_netns_init_sysctl(struct net *net, struct netns_vrf *nn_vrf)
 	if (!table)
 		return -ENOMEM;
 
-	table[0].data = net;
+	table[0].priv = net;
 
 	nn_vrf->ctl_hdr = register_net_sysctl_sz(net, "net/vrf", table,
 						 ARRAY_SIZE(vrf_table));
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index 1721e63240c9..16441c83abf9 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -98,7 +98,7 @@ DEFINE_MUTEX(rps_default_mask_mutex);
 static int rps_default_mask_sysctl(const struct ctl_table *table, int write,
 				   void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct net *net = (struct net *)table->data;
+	struct net *net = table->priv;
 	struct cpumask *mask;
 	int err = 0;
 
@@ -703,7 +703,8 @@ static const struct ctl_table netns_core_table[] = {
 #if IS_ENABLED(CONFIG_RPS)
 	{
 		.procname	= "rps_default_mask",
-		.data		= &init_net,
+		.data		= NULL,
+		.priv		= &init_net,
 		.mode		= 0644,
 		.proc_handler	= rps_default_mask_sysctl
 	},
@@ -828,6 +829,12 @@ static const struct ctl_table *netns_core_table_dup(struct net *net)
 		if (tbl[i].data == &sysctl_wmem_max)
 			break;
 
+		/* Entries without backing storage carry the namespace in priv. */
+		if (tbl[i].priv) {
+			tbl[i].priv = net;
+			continue;
+		}
+
 		tbl[i].data += (char *)net - (char *)&init_net;
 	}
 	for (; i < table_size; ++i)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index b4c415f720b2..5a8d0fcf75e3 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -3511,7 +3511,7 @@ static int ip_rt_gc_elasticity __read_mostly	= 8;
 static int ipv4_sysctl_rtcache_flush(const struct ctl_table *__ctl, int write,
 		void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct net *net = __ctl->data;
+	struct net *net = __ctl->priv;
 
 	if (write) {
 		rt_cache_flush(net);
@@ -3670,7 +3670,7 @@ static __net_init int sysctl_route_net_init(struct net *net)
 		for (i = 1; i < table_size; i++)
 			tbl[i].data += (void *)net - (void *)&init_net;
 	}
-	tbl[0].data = net;
+	tbl[0].priv = net;
 
 	net->ipv4.route_hdr = register_net_sysctl_sz(net, "net/ipv4/route",
 						     tbl, table_size);
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 4226ce923534..e65d67567af9 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -40,7 +40,7 @@ static void set_local_port_range(struct net *net, unsigned int low, unsigned int
 static int ipv4_local_port_range(const struct ctl_table *table, int write,
 				 void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct net *net = table->data;
+	struct net *net = table->priv;
 	int ret;
 	int range[2];
 	struct ctl_table tmp = {
@@ -465,7 +465,7 @@ static int proc_fib_multipath_hash_seed(const struct ctl_table *table, int write
 					loff_t *ppos)
 {
 	struct sysctl_fib_multipath_hash_seed *mphs;
-	struct net *net = table->data;
+	struct net *net = table->priv;
 	struct ctl_table tmp;
 	u32 user_seed;
 	int ret;
@@ -824,7 +824,7 @@ static const struct ctl_table ipv4_net_table[] = {
 	{
 		.procname	= "ip_local_port_range",
 		.maxlen		= 0,
-		.data		= &init_net,
+		.priv		= &init_net,
 		.mode		= 0644,
 		.proc_handler	= ipv4_local_port_range,
 	},
@@ -1213,7 +1213,7 @@ static const struct ctl_table ipv4_net_table[] = {
 	},
 	{
 		.procname	= "fib_multipath_hash_seed",
-		.data		= &init_net,
+		.priv		= &init_net,
 		.maxlen		= sizeof(u32),
 		.mode		= 0644,
 		.proc_handler	= proc_fib_multipath_hash_seed,
@@ -1753,7 +1753,10 @@ static const struct ctl_table *ipv4_net_table_dup(struct net *net)
 		return NULL;
 
 	for (i = 0; i < table_size; i++) {
-		if (table[i].data) {
+		if (table[i].priv) {
+			/* Handler context follows the target namespace. */
+			table[i].priv = net;
+		} else if (table[i].data) {
 			/* Update the variables to point into
 			 * the current struct net
 			 */
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index b8df02173d7a..16058d584989 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -2679,7 +2679,7 @@ static int resize_platform_label_table(struct net *net, size_t limit)
 static int mpls_platform_labels(const struct ctl_table *table, int write,
 				void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct net *net = table->data;
+	struct net *net = table->priv;
 	int platform_labels = net->mpls.platform_labels;
 	int ret;
 	struct ctl_table tmp = *table;
@@ -2746,9 +2746,12 @@ static __net_init int mpls_net_init(struct net *net)
 		return -ENOMEM;
 
 	/* Table data contains only offsets relative to the base of
-	 * the mdev at this point, so make them absolute.
+	 * the mdev at this point, so make them absolute. The
+	 * platform_labels entry has no backing storage and carries the
+	 * namespace in priv instead.
 	 */
-	for (i = 0; i < table_size; i++)
+	table[0].priv = net;
+	for (i = 1; i < table_size; i++)
 		table[i].data = (char *)net + (uintptr_t)table[i].data;
 
 	net->mpls.ctl = register_net_sysctl_sz(net, "net/mpls", table,
-- 
2.25.1


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

* [RFC PATCH 38/39] arm64: move vector-length sysctl context to priv
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (36 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 37/39] net: move namespace " wen.yang
@ 2026-10-28 16:35 ` wen.yang
  2026-10-28 16:35 ` [RFC PATCH 39/39] sysctl: remove the extra pointers and the compatibility bounds read wen.yang
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

The two default-vector-length entries borrow extra1 for a struct vl_info
pointer, which is not a bound, while the handler delegates with a local
int table.

Pass vl_info through priv and leave extra1/extra2 to the numeric bounds.

Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 arch/arm64/kernel/fpsimd.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index ecfa21615f16..97e746bca99b 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -549,15 +549,14 @@ static unsigned int find_supported_vector_length(enum vec_type type,
 static int vec_proc_do_default_vl(const struct ctl_table *table, int write,
 				  void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct vl_info *info = table->extra1;
+	struct vl_info *info = table->priv;
 	enum vec_type type = info->type;
 	int ret;
 	int vl = get_default_vl(type);
-	struct ctl_table tmp_table = {
-		.data = &vl,
-		.maxlen = sizeof(vl),
-	};
+	struct ctl_table tmp_table = *table;
 
+	tmp_table.data = &vl;
+	tmp_table.maxlen = sizeof(vl);
 	ret = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
 	if (ret || !write)
 		return ret;
@@ -578,7 +577,7 @@ static const struct ctl_table sve_default_vl_table[] = {
 		.procname	= "sve_default_vector_length",
 		.mode		= 0644,
 		.proc_handler	= vec_proc_do_default_vl,
-		.extra1		= &vl_info[ARM64_VEC_SVE],
+		.priv		= &vl_info[ARM64_VEC_SVE],
 	},
 };
 
@@ -601,7 +600,7 @@ static const struct ctl_table sme_default_vl_table[] = {
 		.procname	= "sme_default_vector_length",
 		.mode		= 0644,
 		.proc_handler	= vec_proc_do_default_vl,
-		.extra1		= &vl_info[ARM64_VEC_SME],
+		.priv		= &vl_info[ARM64_VEC_SME],
 	},
 };
 
-- 
2.25.1


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

* [RFC PATCH 39/39] sysctl: remove the extra pointers and the compatibility bounds read
  2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
                   ` (37 preceding siblings ...)
  2026-10-28 16:35 ` [RFC PATCH 38/39] arm64: move vector-length " wen.yang
@ 2026-10-28 16:35 ` wen.yang
  38 siblings, 0 replies; 41+ messages in thread
From: wen.yang @ 2026-10-28 16:35 UTC (permalink / raw)
  To: Joel Granados; +Cc: Wen Yang, linux-fsdevel, linux-kernel

From: Wen Yang <wen.yang@linux.dev>

Every entry now carries numeric min/max, so the pointer bounds have no
remaining users. Drop extra1 and extra2 from struct ctl_table, remove the
pointer branch from the range converters, and delete the shared pointer
constants that only existed to initialise those fields.

Suggested-by: Eric W. Biederman <ebiederm@xmission.com>
Suggested-by: Joel Granados <joel.granados@kernel.org>
Signed-off-by: Wen Yang <wen.yang@linux.dev>
---
 fs/proc/proc_sysctl.c  | 26 ++++-------------
 include/linux/sysctl.h | 65 +++++++++++++++++++-----------------------
 kernel/sysctl.c        | 47 ++++++++----------------------
 kernel/time/jiffies.c  |  2 +-
 lib/test_sysctl.c      | 24 ++++++++--------
 5 files changed, 59 insertions(+), 105 deletions(-)

diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 3e65d75710df..dbbc94b61b71 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -1105,7 +1105,6 @@ static int sysctl_err(const char *path, const struct ctl_table *table, char *fmt
 
 static int sysctl_check_table_array(const char *path, const struct ctl_table *table)
 {
-	unsigned int extra;
 	int err = 0;
 
 	if ((table->proc_handler == proc_douintvec) ||
@@ -1117,23 +1116,6 @@ static int sysctl_check_table_array(const char *path, const struct ctl_table *ta
 	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");
-		}
-	}
-
-	/* Numeric bounds are validated alongside the legacy pointer form. */
-	if (table->proc_handler == proc_dou8vec_minmax) {
 		if (table->min > 255U || table->max > 255U)
 			err |= sysctl_err(path, table,
 					 "range value too large for proc_dou8vec_minmax");
@@ -1366,9 +1348,11 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
  * 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
- * XXX: we should eventually modify these to use long min / max [0]
- * [0] https://lkml.kernel.org/87zgpte9o4.fsf@email.froward.int.ebiederm.org
+ * min, max - numeric bounds used by range-checking handlers. Signed handlers
+ *            interpret these values as signed longs. Unrestricted ranges must
+ *            explicitly use the corresponding type limits. Custom handlers
+ *            keep their context in data and can substitute backing storage
+ *            on an invocation-local copy without changing these bounds.
  *
  * Leaf nodes in the sysctl tree will be represented by a single file
  * under /proc; non-leaf nodes are not allowed.
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 225c07faf2c9..9cda53941e22 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -37,27 +37,22 @@ struct ctl_table_root;
 struct ctl_table_header;
 struct ctl_dir;
 
-/* Keep the same order as in fs/proc/proc_sysctl.c */
-#define SYSCTL_ZERO			((void *)&sysctl_vals[0])
-#define SYSCTL_ONE			((void *)&sysctl_vals[1])
-#define SYSCTL_TWO			((void *)&sysctl_vals[2])
-#define SYSCTL_THREE			((void *)&sysctl_vals[3])
-#define SYSCTL_FOUR			((void *)&sysctl_vals[4])
-#define SYSCTL_ONE_HUNDRED		((void *)&sysctl_vals[5])
-#define SYSCTL_TWO_HUNDRED		((void *)&sysctl_vals[6])
-#define SYSCTL_ONE_THOUSAND		((void *)&sysctl_vals[7])
-#define SYSCTL_THREE_THOUSAND		((void *)&sysctl_vals[8])
-#define SYSCTL_INT_MAX			((void *)&sysctl_vals[9])
-
-/* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
-#define SYSCTL_MAXOLDUID		((void *)&sysctl_vals[10])
-#define SYSCTL_NEG_ONE			((void *)&sysctl_vals[11])
-
-extern const int sysctl_vals[];
-
-#define SYSCTL_LONG_ZERO	((void *)&sysctl_long_vals[0])
-#define SYSCTL_LONG_ONE		((void *)&sysctl_long_vals[1])
-#define SYSCTL_LONG_MAX		((void *)&sysctl_long_vals[2])
+/* Numeric constants for sysctl ranges. */
+#define SYSCTL_ZERO			0
+#define SYSCTL_ONE			1
+#define SYSCTL_TWO			2
+#define SYSCTL_THREE			3
+#define SYSCTL_FOUR			4
+#define SYSCTL_ONE_HUNDRED		100
+#define SYSCTL_TWO_HUNDRED		200
+#define SYSCTL_ONE_THOUSAND		1000
+#define SYSCTL_THREE_THOUSAND		3000
+#define SYSCTL_INT_MAX			INT_MAX
+#define SYSCTL_MAXOLDUID		65535
+#define SYSCTL_NEG_ONE			(-1)
+#define SYSCTL_LONG_ZERO		0UL
+#define SYSCTL_LONG_ONE			1UL
+#define SYSCTL_LONG_MAX			LONG_MAX
 
 /*
  *
@@ -72,8 +67,6 @@ extern const int sysctl_vals[];
 #define SYSCTL_USER_TO_KERN(dir) (!!(dir))
 #define SYSCTL_KERN_TO_USER(dir) (!dir)
 
-extern const unsigned long sysctl_long_vals[];
-
 typedef int proc_handler(const struct ctl_table *ctl, int dir, void *buf,
 			 size_t *lenp, loff_t *ppos);
 
@@ -84,16 +77,17 @@ int proc_dobool(const struct ctl_table *ctl, int dir, void *buf, size_t *lenp,
 		loff_t *ppos);
 int proc_dointvec(const struct ctl_table *ctl, int dir, void *buf, size_t *lenp,
 		  loff_t *ppos);
-int proc_dointvec_minmax(const struct ctl_table *ctl, int dir, void *buf,
-			 size_t *lenp, loff_t *ppos);
 int proc_douintvec(const struct ctl_table *ctl, int dir, void *buf, size_t *lenp,
 		   loff_t *ppos);
-int proc_douintvec_minmax(const struct ctl_table *ctl, int dir, void *buf,
-			  size_t *lenp, loff_t *ppos);
-int proc_dou8vec_minmax(const struct ctl_table *ctl, int dir, void *buf,
-			size_t *lenp, loff_t *ppos);
-int proc_doulongvec_minmax(const struct ctl_table *ctl, int dir, void *buf,
-			   size_t *lenp, loff_t *ppos);
+/* Range handlers consume numeric table bounds. */
+int proc_dointvec_minmax(const struct ctl_table *table, int dir,
+			 void *buffer, size_t *lenp, loff_t *ppos);
+int proc_douintvec_minmax(const struct ctl_table *table, int dir,
+			  void *buffer, size_t *lenp, loff_t *ppos);
+int proc_doulongvec_minmax(const struct ctl_table *table, int dir,
+			   void *buffer, size_t *lenp, loff_t *ppos);
+int proc_dou8vec_minmax(const struct ctl_table *table, int dir,
+			void *buffer, size_t *lenp, loff_t *ppos);
 int proc_do_large_bitmap(const struct ctl_table *ctl, int dir, void *buf,
 			 size_t *lenp, loff_t *ppos);
 int proc_do_static_key(const struct ctl_table *ctl, int dir, void *buf,
@@ -116,15 +110,15 @@ int proc_do_static_key(const struct ctl_table *ctl, int dir, void *buf,
 int proc_dointvec_conv(const struct ctl_table *ctl, int dir, void *buf,
 		       size_t *lenp, loff_t *ppos,
 		       int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
-				   int dir, const struct ctl_table *ctl));
+		       int dir, const struct ctl_table *ctl));
 int proc_douintvec_conv(const struct ctl_table *ctl, int dir, void *buf,
 			size_t *lenp, loff_t *ppos,
 			int (*conv)(bool *negp, ulong *u_ptr, uint *k_ptr,
-				    int dir, const struct ctl_table *ctl));
+			int dir, const struct ctl_table *ctl));
 int proc_doulongvec_conv(const struct ctl_table *ctl, int dir, void *buf,
 			 size_t *lenp, loff_t *ppos,
 			 int (*conv)(bool *negp, ulong *u_ptr, ulong *k_ptr,
-				     int dir, const struct ctl_table *ctl));
+			 int dir, const struct ctl_table *ctl));
 
 /*
  * bi-directional converter functions
@@ -140,6 +134,7 @@ int proc_doulongvec_conv(const struct ctl_table *ctl, int dir, void *buf,
  * { return proc_uint_conv(u_ptr, k_ptr, dir, ctl, true,
  *                         custom_u2k, proc_uint_k2u_conv}
  */
+
 int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir,
 		  const struct ctl_table *tbl, bool k_ptr_range_check,
 		  int (*user_to_kern)(const bool *negp, const ulong *u_ptr, int *k_ptr),
@@ -226,8 +221,6 @@ struct ctl_table {
 	umode_t mode;
 	proc_handler *proc_handler;	/* Callback for text formatting */
 	struct ctl_table_poll *poll;
-	void *extra1;			/* Deprecated; being removed */
-	void *extra2;			/* Deprecated; being removed */
 	unsigned long min;
 	unsigned long max;
 	void *priv;			/* Handler context, never used by the core */
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index ab25c75cb3ac..63a8a6b08090 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -22,13 +22,6 @@
 #include <linux/uaccess.h>
 #include <asm/processor.h>
 
-/* shared constants to be used in various sysctls */
-const int sysctl_vals[] = { 0, 1, 2, 3, 4, 100, 200, 1000, 3000, INT_MAX, 65535, -1 };
-EXPORT_SYMBOL(sysctl_vals);
-
-const unsigned long sysctl_long_vals[] = { 0, 1, LONG_MAX };
-EXPORT_SYMBOL_GPL(sysctl_long_vals);
-
 #ifdef CONFIG_SYSCTL
 
 /* Constants used for minimum and maximum */
@@ -423,15 +416,8 @@ int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
 		ret = user_to_kern(u_ptr, &tmp_k);
 		if (ret)
 			return ret;
-		if (tbl->extra1 || tbl->extra2) {
-			if ((tbl->extra1 &&
-			     *(uint *)tbl->extra1 > tmp_k) ||
-			    (tbl->extra2 &&
-			     *(uint *)tbl->extra2 < tmp_k))
-				return -ERANGE;
-		} else if (tbl->min > tmp_k || tbl->max < tmp_k) {
+		if (tbl->min > tmp_k || tbl->max < tmp_k)
 			return -ERANGE;
-		}
 		WRITE_ONCE(*k_ptr, tmp_k);
 	} else
 		return user_to_kern(u_ptr, k_ptr);
@@ -547,19 +533,16 @@ int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir,
 		ret = user_to_kern(negp, u_ptr, &tmp_k);
 		if (ret)
 			return ret;
-		if (tbl->extra1 || tbl->extra2) {
-			if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) ||
-			    (tbl->extra2 && *(int *)tbl->extra2 < tmp_k))
-				return -EINVAL;
-		} else if ((long)tbl->min > tmp_k || (long)tbl->max < tmp_k) {
+		if ((long)tbl->min > tmp_k || (long)tbl->max < tmp_k)
 			return -EINVAL;
-		}
 		WRITE_ONCE(*k_ptr, tmp_k);
 	} else
 		return user_to_kern(negp, u_ptr, k_ptr);
 	return 0;
 }
 
+
+
 static int sysctl_user_to_kern_int_conv(const bool *negp, const ulong *u_ptr,
 					int *k_ptr)
 {
@@ -930,7 +913,7 @@ int proc_douintvec(const struct ctl_table *table, int dir, void *buffer,
  * values from/to the user buffer, treated as an ASCII string.
  *
  * This routine will ensure the values are within the range specified by
- * table->extra1 (min) and table->extra2 (max).
+ * table->min and table->max.
  *
  * Returns: %0 on success or -EINVAL when the range check fails and
  * SYSCTL_USER_TO_KERN(dir) == true
@@ -955,9 +938,8 @@ int proc_dointvec_minmax(const struct ctl_table *table, int dir,
  * strings are not allowed.
  *
  * When changing the kernel variable, this routine will ensure the values
- * are within the range specified by table->extra1 (min) and table->extra2
- * (max). And Check that the values are less than UINT_MAX to avoid having to
- * support wrap around uses from userspace.
+ * are within the range specified by table->min and table->max. Values larger
+ * than UINT_MAX are rejected to avoid wraparound from userspace.
  *
  * Returns: %0 on success or -ERANGE when range check failes and
  * SYSCTL_USER_TO_KERN(dir) == true
@@ -982,7 +964,7 @@ int proc_douintvec_minmax(const struct ctl_table *table, int dir,
  * strings are not allowed.
  *
  * This routine will ensure the values are within the range specified by
- * table->extra1 (min) and table->extra2 (max).
+ * table->min and table->max.
  *
  * Returns: %0 on success or an error on SYSCTL_USER_TO_KERN(dir) == true
  * and the range check fails.
@@ -1047,13 +1029,8 @@ int proc_ulong_conv(ulong *u_ptr, ulong *k_ptr, int dir,
 		ret = user_to_kern(u_ptr, &tmp_k);
 		if (ret)
 			return ret;
-		if (tbl->extra1 || tbl->extra2) {
-			if ((tbl->extra1 && *(ulong *)tbl->extra1 > tmp_k) ||
-			    (tbl->extra2 && *(ulong *)tbl->extra2 < tmp_k))
-				return -ERANGE;
-		} else if (tbl->min > tmp_k || tbl->max < tmp_k) {
+		if (tbl->min > tmp_k || tbl->max < tmp_k)
 			return -ERANGE;
-		}
 		WRITE_ONCE(*k_ptr, tmp_k);
 	} else
 		return user_to_kern(u_ptr, k_ptr);
@@ -1155,7 +1132,7 @@ int proc_doulongvec_conv(const struct ctl_table *table, int dir,
  * values from/to the user buffer, treated as an ASCII string.
  *
  * This routine will ensure the values are within the range specified by
- * table->extra1 (min) and table->extra2 (max).
+ * table->min and table->max.
  *
  * Returns: %0 on success.
  */
@@ -1348,8 +1325,8 @@ int proc_do_static_key(const struct ctl_table *table, int dir,
 		.data   = &val,
 		.maxlen = sizeof(val),
 		.mode   = table->mode,
-		.min    = 0,
-		.max    = 1,
+		.min = 0,
+		.max = 1,
 	};
 
 	if (SYSCTL_USER_TO_KERN(dir) && !capable(CAP_SYS_ADMIN))
diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c
index 8dde5a2cc532..184d4c5a7917 100644
--- a/kernel/time/jiffies.c
+++ b/kernel/time/jiffies.c
@@ -331,7 +331,7 @@ EXPORT_SYMBOL(proc_dointvec_ms_jiffies_minmax);
  * are treated as milliseconds, and converted to jiffies when they are stored.
  *
  * This routine will ensure the values are within the range specified by
- * table->extra1 (min) and table->extra2 (max).
+ * table->min and table->max.
  *
  * Returns 0 on success.
  */
diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c
index 64c82fd53975..ed3547401f41 100644
--- a/lib/test_sysctl.c
+++ b/lib/test_sysctl.c
@@ -258,18 +258,18 @@ static void test_sysctl_calc_match_int_ok(void)
 		int defined;
 		int wanted;
 	} match_int[] = {
-		{ .defined = 0, .wanted = 0 },
-		{ .defined = 1, .wanted = 1 },
-		{ .defined = 2, .wanted = 2 },
-		{ .defined = 3, .wanted = 3 },
-		{ .defined = 4, .wanted = 4 },
-		{ .defined = 100, .wanted = 100 },
-		{ .defined = 200, .wanted = 200 },
-		{ .defined = 1000, .wanted = 1000 },
-		{ .defined = 3000, .wanted = 3000 },
-		{ .defined = INT_MAX, .wanted = INT_MAX },
-		{ .defined = 65535, .wanted = 65535 },
-		{ .defined = -1, .wanted = -1 },
+		{ .defined = SYSCTL_ZERO, .wanted = 0 },
+		{ .defined = SYSCTL_ONE, .wanted = 1 },
+		{ .defined = SYSCTL_TWO, .wanted = 2 },
+		{ .defined = SYSCTL_THREE, .wanted = 3 },
+		{ .defined = SYSCTL_FOUR, .wanted = 4 },
+		{ .defined = SYSCTL_ONE_HUNDRED, .wanted = 100 },
+		{ .defined = SYSCTL_TWO_HUNDRED, .wanted = 200 },
+		{ .defined = SYSCTL_ONE_THOUSAND, .wanted = 1000 },
+		{ .defined = SYSCTL_THREE_THOUSAND, .wanted = 3000 },
+		{ .defined = SYSCTL_INT_MAX, .wanted = INT_MAX },
+		{ .defined = SYSCTL_MAXOLDUID, .wanted = 65535 },
+		{ .defined = SYSCTL_NEG_ONE, .wanted = -1 },
 	};
 
 	for (i = 0; i < ARRAY_SIZE(match_int); i++)
-- 
2.25.1


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

* Re: [RFC PATCH 01/39] sysctl: replace overloaded extra pointers with typed bounds and context
  2026-10-28 16:34 ` [RFC PATCH 01/39] " wen.yang
@ 2026-10-28 21:09   ` David Laight
  0 siblings, 0 replies; 41+ messages in thread
From: David Laight @ 2026-10-28 21:09 UTC (permalink / raw)
  To: wen.yang; +Cc: Joel Granados, linux-fsdevel, linux-kernel

On Thu, 29 Oct 2026 00:34:40 +0800
wen.yang@linux.dev wrote:

> To: Joel Granados <joel.granados@kernel.org>
> Cc: Wen Yang <wen.yang@linux.dev>,  linux-fsdevel@vger.kernel.org,  linux-kernel@vger.kernel.org
> Subject: [RFC PATCH 01/39] sysctl: replace overloaded extra pointers with typed bounds and context
> Date: Thu, 29 Oct 2026 00:34:40 +0800
> 
> From: Wen Yang <wen.yang@linux.dev>
> 
> struct ctl_table has two untyped misc pointers, extra1 and extra2, whose
> meaning depends on the handler that reads them. In-tree use has grown to
> cover at least seven unrelated jobs: a fixed bound, a bound read through a
> pointer on every call, an object or namespace pointer, a small integer
> cookie encoded into the pointer, a lock signal, NULL standing for "no
> bound", and a value the core itself casts to unsigned int while validating
> a u8 range. Nothing in the entry says which job applies, so readers and
> writers have to infer it from the handler, and the core itself gets it
> wrong where it treats extra1 as an unsigned int.
> 
> The overloading is also a memory-safety hazard. An entry that stores
> handler context in data cannot be handed to a standard handler without
> first restoring the real storage, because proc_dointvec() writes through
> data. Every call site has to remember the restore, and one that forgets
> turns a user-supplied integer into a write over a kernel pointer. nf_log
> reaches the same problem from the other side: it replaces data with a
> local buffer inside a single handler, so data means two things within one
> function.
> 
> Replace both jobs with two fields that cannot be confused:
> 
>   - min/max hold numeric bounds by value. Signed handlers read them as
>     signed longs; an absent endpoint is the type limit, not NULL, so zero
>     stays a real bound. A range that changes at run time is refreshed
>     before every call instead of being snapshotted at registration.
> 
>   - priv holds handler context. The core never reads it, standard handlers
>     never touch it, and registration rejects it on a standard handler so
>     context cannot be silently ignored. data and maxlen keep the meaning
>     their handler gives them, including proc_dostring() name lengths and
>     proc_do_large_bitmap() bit counts.
> 
> The range converters read min/max whenever extra1 and extra2 are both
> clear, and keep reading the pointers while they are set. That lets callers
> migrate one subsystem at a time without renaming handlers or adding a
> parallel API: the existing proc_*_minmax entry points keep their names and
> gain numeric semantics as entries drop their extras. Entries that already
> relied on NULL meaning "no bound" now state their type limits explicitly,
> so their behaviour does not change and the meaning of zero stays
> unambiguous.
> 
> This patch is the refactor itself. The next patch converts the debug test
> module to numeric bounds as the first user, and the KUnit suite after it
> makes the contract executable; everything after those is migration of
> existing users and adds no new semantics: fixed bounds become numeric
> values, run-time bounds are refreshed per call, context moves off data, and
> the extra pointers and the compatibility read are removed once the last
> user is gone. Owners that can still be recovered from data keep using
> container_of(); priv is only for entries where no derivation exists.
> 
> Replacing the two pointers with unsigned long min/max follows Eric
> Biederman's suggestion; priv is added here because the same pair of fields
> also carries handler context, which a numeric bound cannot express.
> 
> Suggested-by: Eric W. Biederman <ebiederm@xmission.com>
> Suggested-by: Joel Granados <joel.granados@kernel.org>
> Signed-off-by: Wen Yang <wen.yang@linux.dev>
...
> 
> diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c
> index b5c2abd12432..294897f3fbb5 100644
> --- a/arch/powerpc/platforms/pseries/mobility.c
> +++ b/arch/powerpc/platforms/pseries/mobility.c
> @@ -10,6 +10,7 @@
>  #define pr_fmt(fmt) "mobility: " fmt
>  
>  #include <linux/cpu.h>
> +#include <linux/limits.h>
>  #include <linux/kernel.h>
>  #include <linux/kobject.h>
>  #include <linux/nmi.h>
> @@ -61,6 +62,8 @@ static const struct ctl_table nmi_wd_lpm_factor_ctl_table[] = {
>  		.maxlen		= sizeof(int),
>  		.mode		= 0644,
>  		.proc_handler	= proc_douintvec_minmax,
> +		.min		= 0,
> +		.max		= UINT_MAX,
>  	},

You could save a lot of churn by not adding the '.min = 0' line
and making the function treat the 'silly' case where both min and max
are zero as meaning 'no upper bound'.

As well as saving a lot of diffs, it will be less error prone.

David

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

end of thread, other threads:[~2026-10-28 21:09 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-28 16:34 [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context wen.yang
2026-10-28 16:34 ` [RFC PATCH 01/39] " wen.yang
2026-10-28 21:09   ` David Laight
2026-10-28 16:34 ` [RFC PATCH 02/39] test_sysctl: convert bounds to numeric values wen.yang
2026-10-28 16:34 ` [RFC PATCH 03/39] sysctl: add KUnit coverage for numeric bounds and handlers wen.yang
2026-10-28 16:34 ` [RFC PATCH 04/39] sysctl: cover runtime ranges and context lifecycles in KUnit wen.yang
2026-10-28 16:34 ` [RFC PATCH 05/39] sysctl: cover priv in KUnit and the debug test module wen.yang
2026-10-28 16:34 ` [RFC PATCH 06/39] sysctl: convert core table bounds to numeric values wen.yang
2026-10-28 16:34 ` [RFC PATCH 07/39] sysctl: store architecture sysctl bounds by value wen.yang
2026-10-28 16:34 ` [RFC PATCH 08/39] sysctl: store crypto " wen.yang
2026-10-28 16:34 ` [RFC PATCH 09/39] sysctl: store device " wen.yang
2026-10-28 16:34 ` [RFC PATCH 10/39] sysctl: store filesystem " wen.yang
2026-10-28 16:34 ` [RFC PATCH 11/39] sysctl: store message-queue " wen.yang
2026-10-28 16:34 ` [RFC PATCH 12/39] sysctl: store kernel " wen.yang
2026-10-28 16:34 ` [RFC PATCH 13/39] sysctl: store memory-management " wen.yang
2026-10-28 16:34 ` [RFC PATCH 14/39] sysctl: store IPv6 scalar " wen.yang
2026-10-28 16:34 ` [RFC PATCH 15/39] sysctl: store netfilter " wen.yang
2026-10-28 16:34 ` [RFC PATCH 16/39] sysctl: store RPC " wen.yang
2026-10-28 16:34 ` [RFC PATCH 17/39] sysctl: store remaining network " wen.yang
2026-10-28 16:34 ` [RFC PATCH 18/39] sysctl: store security " wen.yang
2026-10-28 16:34 ` [RFC PATCH 19/39] io_uring: store its " wen.yang
2026-10-28 16:34 ` [RFC PATCH 20/39] sysctl: refresh dynamic scheduler and PID bounds per call wen.yang
2026-10-28 16:35 ` [RFC PATCH 21/39] sysctl: refresh device-dependent " wen.yang
2026-10-28 16:35 ` [RFC PATCH 22/39] sysctl: refresh namespace IPC " wen.yang
2026-10-28 16:35 ` [RFC PATCH 23/39] yama: preserve scope locking with numeric sysctl bounds wen.yang
2026-10-28 16:35 ` [RFC PATCH 24/39] net: refresh core sysctl limits without bound pointers wen.yang
2026-10-28 16:35 ` [RFC PATCH 25/39] sctp: resolve numeric ranges from the current namespace wen.yang
2026-10-28 16:35 ` [RFC PATCH 26/39] rds: preserve live reconnect limits with numeric bounds wen.yang
2026-10-28 16:35 ` [RFC PATCH 27/39] ipv4: preserve live and vector bounds with numeric sysctls wen.yang
2026-10-28 16:35 ` [RFC PATCH 28/39] net: refresh fragment and remaining dynamic sysctl bounds wen.yang
2026-10-28 16:35 ` [RFC PATCH 29/39] neighbour: derive sysctl ownership independently of bounds wen.yang
2026-10-28 16:35 ` [RFC PATCH 30/39] net: resolve sysctl owners from their backing data wen.yang
2026-10-28 16:35 ` [RFC PATCH 31/39] ipvs: derive sysctl ownership from numeric storage wen.yang
2026-10-28 16:35 ` [RFC PATCH 32/39] sysctl: remove obsolete nr_open bound variables wen.yang
2026-10-28 16:35 ` [RFC PATCH 33/39] ipv6: move address sysctl context to priv wen.yang
2026-10-28 16:35 ` [RFC PATCH 34/39] ipv4: move device " wen.yang
2026-10-28 16:35 ` [RFC PATCH 35/39] netfilter: move logger " wen.yang
2026-10-28 16:35 ` [RFC PATCH 36/39] parport: move device " wen.yang
2026-10-28 16:35 ` [RFC PATCH 37/39] net: move namespace " wen.yang
2026-10-28 16:35 ` [RFC PATCH 38/39] arm64: move vector-length " wen.yang
2026-10-28 16:35 ` [RFC PATCH 39/39] sysctl: remove the extra pointers and the compatibility bounds read wen.yang

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

all inboxes | Powered by JetHome®