Linux filesystem development
 help / color / mirror / Atom feed
From: David Laight <david.laight.linux@gmail.com>
To: wen.yang@linux.dev
Cc: Joel Granados <joel.granados@kernel.org>,
	linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH 00/39] sysctl: replace overloaded extra pointers with typed bounds and context
Date: Thu, 29 Oct 2026 10:58:37 +0000	[thread overview]
Message-ID: <20261029105837.2f8bee67@pumpkin> (raw)
In-Reply-To: <cover.1793204511.git.wen.yang@linux.dev>

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

> 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.

Can you remove the little-used 'priv' parameter by making the extra
information just follow the table entry (nested structure) and have
the relevant functions use container_of() to get the outer structure
and extra values?

David


      parent reply	other threads:[~2026-10-29 10:58 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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
2026-10-29 10:58 ` David Laight [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261029105837.2f8bee67@pumpkin \
    --to=david.laight.linux@gmail.com \
    --cc=joel.granados@kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=wen.yang@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox