Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 00/24] binfmt_misc: write access fixes, RCU handler lookup and cleanups
@ 2026-07-10  9:33 Christian Brauner
  2026-07-10  9:33 ` [PATCH v3 01/24] binfmt_misc: restore write access when removing an entry Christian Brauner
                   ` (23 more replies)
  0 siblings, 24 replies; 25+ messages in thread
From: Christian Brauner @ 2026-07-10  9:33 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: Alexander Viro, Christian Brauner, Jan Kara, linux-mm,
	Farid Zakaria, jannh, stable

The first two patches fix two i_writecount imbalances on
MISC_FMT_OPEN_FILE interpreter files that turned up while auditing
the file for the rework below and are marked for stable: removing an
entry never restored the write access denied by open_exec() at
registration, leaving the interpreter unwritable until its inode gets
evicted, and the write denial taken on the interpreter clone during
exec is not paired with the FMODE_FSNOTIFY_HSM aware release the exec
machinery uses, so pre-content watches make execs leak write denials.

Also, a register string whose delimiter is one of the flag characters
('P', 'O', 'C', 'F') makes the flag scan in create_entry() run past the
end of the register buffer. Reject such a delimiter up front.

The rest reworks the locking and tidies the file up.

The current rwlock protects very little. Entries are immutable after
publication except for the Enabled bit which is already toggled
locklessly via set_bit()/clear_bit() and entry lifetime is already
handled by the users refcount. The read lock's only remaining job is to
make "the entry is still linked" and "take a reference" atomic with
respect to the unlink sites.

So make the lookup an RCU walk that acquires a reference via
refcount_inc_not_zero() and free entries via kfree_rcu(). The removal
paths need to detect whether an entry has already been unlinked and
rely on list_del_init() reinitialization for that today, but
reinitializing the forward pointer of a removed entry would make a
concurrent lockless walker standing on it loop indefinitely. hlists
support exactly this pattern: hlist_del_init_rcu() keeps the forward
pointer of a removed entry intact for concurrent walkers and only
zeroes ->pprev with hlist_unhashed() serving as the linked test. Hence
the third patch converts the entry list to an hlist so the RCU
conversion in the fourth is a pure locking change.

Writers remain serialized by the inode lock of the root dentry with
one exception. Handler removal semantics are unchanged. An exec that
acquired a reference just before its handler was unregistered already
completes with the removed handler today. The read lock never protected
against that, it only made the window smaller.

With this an exec that matches no binfmt_misc entry no longer writes
to any shared cacheline at all.

The fifth patch annotates the long-standing lockless ->enabled accesses
for KCSAN and the three patches after it make the entry flags proper
enums and give struct binfmt_misc_entry a name that isn't Node.

The remaining patches are a cleanup pass over the whole file: remove
the VERBOSE_STATUS and USE_DEBUG compile-time toggles, convert the
entry file to seq_file, factor out entry matching, entry removal and
the register string field parsing, make the entry/register string
allocation a flexible array member, give the parse_command() results
names, let cleanup.h unwind the entry registration and exec error paths
and prune the include list down to what is used. Aside from
seq_lseek() now bounding seeks on entry files and the ETXTBSY
propagation in the second patch the cleanups have no user-visible
effect.

The penultimate patch adds what the comment in remove_binfmt_handler()
had been suggesting for years: entries can now be removed via unlink(2)
in addition to the -1 write. The status and register control files
refuse removal.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
Changes in v3:
- Reject a register string delimiter that is also a flag character.
  A flag-char delimiter made the create_entry() flag scan read past
  the end of the register buffer. Stable-marked, added as the last
  patch.
- Link to v2: https://patch.msgid.link/20260710-work-binfmt_misc-locking-v2-0-2a1c3d4126a7@kernel.org

Changes in v2:
- Allow removing entries via unlink(2).
- Add two stable-marked fixes restoring i_writecount balance for
  MISC_FMT_OPEN_FILE interpreter files, first so they apply to
  mainline directly.
- Turn the entry bit numbers and behavior flags into proper enums and
  rename Node to struct binfmt_misc_entry.
- Add a cleanup pass over the whole file: remove the VERBOSE_STATUS
  and USE_DEBUG toggles, convert the entry file to seq_file, factor
  out entry matching, entry removal and register string parsing, use
  a flexible array member for the register string, name the
  parse_command() results, use cleanup.h for the entry error
  unwinding in registration and exec and prune the include list.
- Link to v1: https://patch.msgid.link/20260708-work-binfmt_misc-locking-v1-0-a009dd5b56db@kernel.org

---
Christian Brauner (24):
      binfmt_misc: restore write access when removing an entry
      binfmt_misc: use exe_file_deny_write_access() for the interpreter clone
      binfmt_misc: reject a flag character as the field delimiter
      binfmt_misc: convert entry list to an hlist
      binfmt_misc: use RCU for the handler lookup
      binfmt_misc: annotate racy accesses to ->enabled
      binfmt_misc: turn the entry bit numbers into a proper enum
      binfmt_misc: turn the entry behavior flags into an enum
      binfmt_misc: rename Node to struct binfmt_misc_entry
      binfmt_misc: remove the VERBOSE_STATUS toggle
      binfmt_misc: use print_hex_dump_debug() for the register debug output
      binfmt_misc: convert the entry file to seq_file
      binfmt_misc: factor out the entry matching
      binfmt_misc: rename load_binfmt_misc() to current_binfmt_misc()
      binfmt_misc: return errors directly in load_misc_binary()
      binfmt_misc: give the parse_command() results names
      binfmt_misc: factor out the entry removal
      binfmt_misc: simplify check_special_flags()
      binfmt_misc: use a flexible array member for the register string
      binfmt_misc: split the field parsing out of create_entry()
      binfmt_misc: use __free(kfree) in bm_register_write()
      binfmt_misc: assorted small cleanups
      binfmt_misc: include what is used
      binfmt_misc: allow removing entries via unlink(2)

 Documentation/admin-guide/binfmt-misc.rst |   3 +-
 fs/binfmt_misc.c                          | 839 +++++++++++++++---------------
 include/linux/binfmts.h                   |   4 +-
 kernel/user.c                             |   4 +-
 4 files changed, 433 insertions(+), 417 deletions(-)
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260708-work-binfmt_misc-locking-15347df12ec8



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

end of thread, other threads:[~2026-07-10  9:34 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-10  9:33 [PATCH v3 00/24] binfmt_misc: write access fixes, RCU handler lookup and cleanups Christian Brauner
2026-07-10  9:33 ` [PATCH v3 01/24] binfmt_misc: restore write access when removing an entry Christian Brauner
2026-07-10  9:33 ` [PATCH v3 02/24] binfmt_misc: use exe_file_deny_write_access() for the interpreter clone Christian Brauner
2026-07-10  9:33 ` [PATCH v3 03/24] binfmt_misc: reject a flag character as the field delimiter Christian Brauner
2026-07-10  9:33 ` [PATCH v3 04/24] binfmt_misc: convert entry list to an hlist Christian Brauner
2026-07-10  9:33 ` [PATCH v3 05/24] binfmt_misc: use RCU for the handler lookup Christian Brauner
2026-07-10  9:33 ` [PATCH v3 06/24] binfmt_misc: annotate racy accesses to ->enabled Christian Brauner
2026-07-10  9:33 ` [PATCH v3 07/24] binfmt_misc: turn the entry bit numbers into a proper enum Christian Brauner
2026-07-10  9:33 ` [PATCH v3 08/24] binfmt_misc: turn the entry behavior flags into an enum Christian Brauner
2026-07-10  9:33 ` [PATCH v3 09/24] binfmt_misc: rename Node to struct binfmt_misc_entry Christian Brauner
2026-07-10  9:33 ` [PATCH v3 10/24] binfmt_misc: remove the VERBOSE_STATUS toggle Christian Brauner
2026-07-10  9:33 ` [PATCH v3 11/24] binfmt_misc: use print_hex_dump_debug() for the register debug output Christian Brauner
2026-07-10  9:33 ` [PATCH v3 12/24] binfmt_misc: convert the entry file to seq_file Christian Brauner
2026-07-10  9:33 ` [PATCH v3 13/24] binfmt_misc: factor out the entry matching Christian Brauner
2026-07-10  9:33 ` [PATCH v3 14/24] binfmt_misc: rename load_binfmt_misc() to current_binfmt_misc() Christian Brauner
2026-07-10  9:33 ` [PATCH v3 15/24] binfmt_misc: return errors directly in load_misc_binary() Christian Brauner
2026-07-10  9:33 ` [PATCH v3 16/24] binfmt_misc: give the parse_command() results names Christian Brauner
2026-07-10  9:33 ` [PATCH v3 17/24] binfmt_misc: factor out the entry removal Christian Brauner
2026-07-10  9:33 ` [PATCH v3 18/24] binfmt_misc: simplify check_special_flags() Christian Brauner
2026-07-10  9:33 ` [PATCH v3 19/24] binfmt_misc: use a flexible array member for the register string Christian Brauner
2026-07-10  9:33 ` [PATCH v3 20/24] binfmt_misc: split the field parsing out of create_entry() Christian Brauner
2026-07-10  9:33 ` [PATCH v3 21/24] binfmt_misc: use __free(kfree) in bm_register_write() Christian Brauner
2026-07-10  9:33 ` [PATCH v3 22/24] binfmt_misc: assorted small cleanups Christian Brauner
2026-07-10  9:33 ` [PATCH v3 23/24] binfmt_misc: include what is used Christian Brauner
2026-07-10  9:33 ` [PATCH v3 24/24] binfmt_misc: allow removing entries via unlink(2) Christian Brauner

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