From: Christian Brauner Date: Fri, 10 Jul 2026 15:19:40 +0000 (+0200) Subject: Merge patch series "binfmt_misc: write access fixes, RCU handler lookup and cleanups" X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=caff00322b213a1dbaaaf43487624353a0930fd2;p=thirdparty%2Flinux.git Merge patch series "binfmt_misc: write access fixes, RCU handler lookup and cleanups" Christian Brauner says: binfmt_misc: write access fixes, RCU handler lookup and cleanups 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. * patches from https://patch.msgid.link/20260710-work-binfmt_misc-locking-v3-0-a162f7cb58d6@kernel.org: (24 commits) binfmt_misc: allow removing entries via unlink(2) binfmt_misc: include what is used binfmt_misc: assorted small cleanups binfmt_misc: use __free(kfree) in bm_register_write() binfmt_misc: split the field parsing out of create_entry() binfmt_misc: use a flexible array member for the register string binfmt_misc: simplify check_special_flags() binfmt_misc: factor out the entry removal binfmt_misc: give the parse_command() results names binfmt_misc: return errors directly in load_misc_binary() binfmt_misc: rename load_binfmt_misc() to current_binfmt_misc() binfmt_misc: factor out the entry matching binfmt_misc: convert the entry file to seq_file binfmt_misc: use print_hex_dump_debug() for the register debug output binfmt_misc: remove the VERBOSE_STATUS toggle binfmt_misc: rename Node to struct binfmt_misc_entry binfmt_misc: turn the entry behavior flags into an enum binfmt_misc: turn the entry bit numbers into a proper enum binfmt_misc: annotate racy accesses to ->enabled binfmt_misc: use RCU for the handler lookup ... Link: https://patch.msgid.link/20260710-work-binfmt_misc-locking-v3-0-a162f7cb58d6@kernel.org Signed-off-by: Christian Brauner (Amutable) --- caff00322b213a1dbaaaf43487624353a0930fd2