FAILED: patch "[PATCH] binfmt_misc: restore write access when removing an entry" failed to apply to 6.12-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x db1856ea9196cf6e015d12199a34c0b9313c7bfa
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026080511-putdown-richly-6ef6@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From db1856ea9196cf6e015d12199a34c0b9313c7bfa Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Fri, 10 Jul 2026 11:33:02 +0200
Subject: [PATCH] binfmt_misc: restore write access when removing an entry
Registering an entry with the MISC_FMT_OPEN_FILE flag opens the interpreter via open_exec() which denies write access to it for as long as the entry exists. Removing the entry closes the interpreter file via filp_close() but never restores write access, leaving the inode's i_writecount permanently negative. Opening the interpreter for writing keeps failing with ETXTBSY long after the entry is gone until the inode is evicted from the inode cache.
Commit 90f601b497d7 ("binfmt_misc: restore write access before closing files opened by open_exec()") fixed the same imbalance in the error path of bm_register_write() but the actual removal path has been leaking the write denial since the introduction of the flag.
Restore write access in put_binfmt_handler() before closing the interpreter file.
fs/binfmt_misc.c +3 -1
@@ -162,8 +162,10 @@ static Node *get_binfmt_handler(struct binfmt_misc *misc,static void put_binfmt_handler (Node *e) {if (refcount_dec_and_test (&e->users)) { -if (e->flags &MISC_FMT_OPEN_FILE ) +if (e->flags &MISC_FMT_OPEN_FILE ) { +exe_file_allow_write_access (e->interp_file);filp_close (e->interp_file,NULL ); + }kfree (e); } }
Sasha Levin
maintainerPATCH 1/4 fsnotify, lsm: Decouple fsnotify from lsm
From: Song Liu <[email protected]>
[ Upstream commit 1cda52f1b4611f4daa9d89e69d9428fb4137dc3f ]
Currently, fsnotify_open_perm() is called from security_file_open(). This is a a bit unexpected and creates otherwise unnecessary dependency of CONFIG_FANOTIFY_ACCESS_PERMISSIONS on CONFIG_SECURITY. Fix this by calling fsnotify_open_perm() directly.
Stable-dep-of: db1856ea9196 ("binfmt_misc: restore write access when removing an entry")
--- fs/notify/fanotify/Kconfig | 1 - fs/open.c | 4 ++++ security/security.c | 9 +-------- 3 files changed, 5 insertions(+), 9 deletions(-)
fs/notify/fanotify/Kconfig +0 -1
@@ -15,7 +15,6 @@ config FANOTIFY config FANOTIFY_ACCESS_PERMISSIONS bool "fanotify permissions checking" depends on FANOTIFY - depends on SECURITY default n help Say Y here is you want fanotify listeners to be able to make permissions
fs/open.c +4 -0
@@ -940,6 +940,10 @@ static int do_dentry_open(struct file *f,if (error) goto cleanup_all; + error =fsnotify_open_perm (f); +if (error) + goto cleanup_all; + error =break_lease (file_inode (f), f->f_flags);if (error) goto cleanup_all;
security/security.c +1 -8
@@ -19,7 +19,6 @@#include <linux/kernel.h> #include <linux/kernel_read_file.h> #include <linux/lsm_hooks.h> -#include <linux/fsnotify.h> #include <linux/mman.h> #include <linux/mount.h> #include <linux/personality.h>
@@ -3214,13 +3213,7 @@ int security_file_receive(struct file *file)*/ int security_file_open (struct file *file) { -int ret; - - ret =call_int_hook (file_open, file); -if (ret) -return ret; - -return fsnotify_open_perm (file); +return call_int_hook (file_open, file); }/**
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 fsnotify: opt-in for permission events at file open time
From: Amir Goldstein <[email protected]>
[ Upstream commit a94204f4d48e28a711b7ed10399f749286c433e3 ]
Legacy inotify/fanotify listeners can add watches for events on inode, parent or mount and expect to get events (e.g. FS_MODIFY) on files that were already open at the time of setting up the watches.
fanotify permission events are typically used by Anti-malware sofware, that is watching the entire mount and it is not common to have more that one Anti-malware engine installed on a system.
To reduce the overhead of the fsnotify_file_perm() hooks on every file access, relax the semantics of the legacy FAN_ACCESS_PERM event to generate events only if there were *any* permission event listeners on the filesystem at the time that the file was opened.
The new semantic is implemented by extending the FMODE_NONOTIFY bit into two FMODE_NONOTIFY_* bits, that are used to store a mode for which of the events types to report.
This is going to apply to the new fanotify pre-content events in order to reduce the cost of the new pre-content event vfs hooks.
[Thanks to Bert Karwatzki <[email protected]> for reporting a bug in this code with CONFIG_FANOTIFY_ACCESS_PERMISSIONS disabled]
Stable-dep-of: db1856ea9196 ("binfmt_misc: restore write access when removing an entry")
--- fs/notify/fsnotify.c | 38 +++++++++++++++++++++++++++++++++++ fs/open.c | 8 +++++++- include/linux/fs.h | 43 +++++++++++++++++++++++++++++++++++----- include/linux/fsnotify.h | 39 +++++++++++++++++++++--------------- 4 files changed, 106 insertions(+), 22 deletions(-)
fs/notify/fsnotify.c +38 -0
@@ -630,6 +630,44 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, }EXPORT_SYMBOL_GPL (fsnotify); +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS +/* +* At open time we check fsnotify_sb_has_priority_watchers() and set the +* FMODE_NONOTIFY_ mode bits accordignly. +* Later, fsnotify permission hooks do not check if there are permission event +* watches, but that there were permission event watches at open time. +*/ +void file_set_fsnotify_mode (struct file *file) +{ +struct super_block *sb = file->f_path.dentry->d_sb; + +/* Is it a file opened by fanotify? */ +if (FMODE_FSNOTIFY_NONE (file->f_mode)) +return ; + +/* +* Permission events is a super set of pre-content events, so if there +* are no permission event watchers, there are also no pre-content event +* watchers and this is implied from the single FMODE_NONOTIFY_PERM bit. +*/ +if (likely (!fsnotify_sb_has_priority_watchers (sb, +FSNOTIFY_PRIO_CONTENT ))) { + file->f_mode |=FMODE_NONOTIFY_PERM ; +return ; + } + +/* +* If there are permission event watchers but no pre-content event +* watchers, set FMODE_NONOTIFY | FMODE_NONOTIFY_PERM to indicate that. +*/ +if (likely (!fsnotify_sb_has_priority_watchers (sb, +FSNOTIFY_PRIO_PRE_CONTENT ))) { + file->f_mode |=FMODE_NONOTIFY |FMODE_NONOTIFY_PERM ; +return ; + } +} +#endif +static __init int fsnotify_init (void ) {int ret;
fs/open.c +7 -1
@@ -912,7 +912,7 @@ static int do_dentry_open(struct file *f, f->f_sb_err =file_sample_sb_err (f);if (unlikely (f->f_flags &O_PATH )) { - f->f_mode =FMODE_PATH |FMODE_OPENED ; + f->f_mode =FMODE_PATH |FMODE_OPENED |FMODE_NONOTIFY ; f->f_op = &empty_fops;return 0 ; }
@@ -940,6 +940,12 @@ static int do_dentry_open(struct file *f,if (error) goto cleanup_all; +/* +* Set FMODE_NONOTIFY_* bits according to existing permission watches. +* If FMODE_NONOTIFY was already set for an fanotify fd, this doesn't +* change anything. +*/ +file_set_fsnotify_mode (f); error =fsnotify_open_perm (f);if (error) goto cleanup_all;
include/linux/fs.h +38 -5
57 lines not on this page · raw message
include/linux/fsnotify.h +23 -16
@@ -108,38 +108,35 @@ static inline void fsnotify_dentry(struct dentry *dentry, __u32 mask)fsnotify_parent (dentry, mask, dentry,FSNOTIFY_EVENT_DENTRY ); } -static inline int fsnotify_file (struct file *file,__u32 mask) +static inline int fsnotify_path (const struct path *path,__u32 mask) { -const struct path *path; +return fsnotify_parent (path->dentry, mask, path,FSNOTIFY_EVENT_PATH ); +} +static inline int fsnotify_file (struct file *file,__u32 mask) +{/* * FMODE_NONOTIFY are fds generated by fanotify itself which should not * generate new events. We also don't want to generate events for * FMODE_PATH fds (involves open & close events) as they are just * handle creation / destruction events and not "real" file events. */ -if (file->f_mode & (FMODE_NONOTIFY |FMODE_PATH )) +if (FMODE_FSNOTIFY_NONE (file->f_mode))return 0 ; - path = &file->f_path; -/* Permission events require group prio >= FSNOTIFY_PRIO_CONTENT */ -if (mask &ALL_FSNOTIFY_PERM_EVENTS && - !fsnotify_sb_has_priority_watchers (path->dentry->d_sb, -FSNOTIFY_PRIO_CONTENT )) -return 0 ; - -return fsnotify_parent (path->dentry, mask, path,FSNOTIFY_EVENT_PATH ); +return fsnotify_path (&file->f_path, mask); }#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + +void file_set_fsnotify_mode (struct file *file); +/* * fsnotify_file_area_perm - permission hook before access to file range */ static inline int fsnotify_file_area_perm (struct file *file,int perm_mask,const loff_t *ppos,size_t count) { -__u32 fsnotify_mask =FS_ACCESS_PERM ; -/* * filesystem may be modified in the context of permission events * (e.g. by HSM filling a file on access), so sb freeze protection
@@ -150,7 +147,10 @@ static inline int fsnotify_file_area_perm(struct file *file, int perm_mask,if (!(perm_mask &MAY_READ ))return 0 ; -return fsnotify_file (file, fsnotify_mask); +if (likely (!FMODE_FSNOTIFY_PERM (file->f_mode))) +return 0 ; + +return fsnotify_path (&file->f_path,FS_ACCESS_PERM ); }/*
@@ -168,16 +168,23 @@ static inline int fsnotify_open_perm(struct file *file) {int ret; +if (likely (!FMODE_FSNOTIFY_PERM (file->f_mode))) +return 0 ; +if (file->f_flags & __FMODE_EXEC) { - ret =fsnotify_file (file,FS_OPEN_EXEC_PERM ); + ret =fsnotify_path (&file->f_path,FS_OPEN_EXEC_PERM );if (ret)return ret; } -return fsnotify_file (file,FS_OPEN_PERM ); +return fsnotify_path (&file->f_path,FS_OPEN_PERM ); }#else +static inline void file_set_fsnotify_mode (struct file *file) +{ +} +static inline int fsnotify_file_area_perm (struct file *file,int perm_mask,const loff_t *ppos,size_t count) {
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 fs: don't block write during exec on pre-content watched files
In reply to Sasha LevinFrom: Amir Goldstein <[email protected]>
[ Upstream commit 0357ef03c94ef835bd44a0658b8edb672a9dbf51 ]
Commit 2a010c412853 ("fs: don't block i_writecount during exec") removed the legacy behavior of getting ETXTBSY on attempt to open and executable file for write while it is being executed.
This commit was reverted because an application that depends on this legacy behavior was broken by the change.
We need to allow HSM writing into executable files while executed to fill their content on-the-fly.
To that end, disable the ETXTBSY legacy behavior for files that are watched by pre-content events.
This change is not expected to cause regressions with existing systems which do not have any pre-content event listeners.
Stable-dep-of: db1856ea9196 ("binfmt_misc: restore write access when removing an entry")
--- fs/binfmt_elf.c | 4 ++-- fs/binfmt_elf_fdpic.c | 4 ++-- fs/exec.c | 8 ++++---- include/linux/fs.h | 22 ++++++++++++++++++++++ kernel/fork.c | 12 ++++++------ 5 files changed, 36 insertions(+), 14 deletions(-)
fs/binfmt_elf.c +2 -2
@@ -1257,7 +1257,7 @@ static int load_elf_binary(struct linux_binprm *bprm) } reloc_func_desc = interp_load_addr; -allow_write_access (interpreter); +exe_file_allow_write_access (interpreter);fput (interpreter);kfree (interp_elf_ex);
@@ -1371,7 +1371,7 @@ static int load_elf_binary(struct linux_binprm *bprm)kfree (interp_elf_ex);kfree (interp_elf_phdata); out_free_file: -allow_write_access (interpreter); +exe_file_allow_write_access (interpreter);if (interpreter)fput (interpreter); out_free_ph:
fs/binfmt_elf_fdpic.c +2 -2
@@ -398,7 +398,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) goto error; } -allow_write_access (interpreter); +exe_file_allow_write_access (interpreter);fput (interpreter); interpreter =NULL ; }
@@ -471,7 +471,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) error:if (interpreter) { -allow_write_access (interpreter); +exe_file_allow_write_access (interpreter);fput (interpreter); }kfree (interpreter_name);
fs/exec.c +4 -4
@@ -912,7 +912,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) path_noexec(&file->f_path))return ERR_PTR (-EACCES ); - err =deny_write_access (file); + err =exe_file_deny_write_access (file);if (err)return ERR_PTR (err);
@@ -927,7 +927,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)* Returns ERR_PTR on failure or allocated struct file on success. * * As this is a wrapper for the internal do_open_execat(), callers -* must call allow_write_access() before fput() on release. Also see +* must call exe_file_allow_write_access() before fput() on release. Also see * do_close_execat(). */ struct file *open_exec (const char *name)
@@ -1501,7 +1501,7 @@ static void do_close_execat(struct file *file) {if (!file)return ; -allow_write_access (file); +exe_file_allow_write_access (file);fput (file); }
@@ -1835,7 +1835,7 @@ static int exec_binprm(struct linux_binprm *bprm) bprm->file = bprm->interpreter; bprm->interpreter =NULL ; -allow_write_access (exec); +exe_file_allow_write_access (exec);if (unlikely (bprm->have_execfd)) {if (bprm->executable) {fput (exec);
include/linux/fs.h +22 -0
@@ -3085,6 +3085,28 @@ static inline void allow_write_access(struct file *file)if (file)atomic_inc (&file_inode (file)->i_writecount); } + +/* +* Do not prevent write to executable file when watched by pre-content events. +* +* Note that FMODE_FSNOTIFY_HSM mode is set depending on pre-content watches at +* the time of file open and remains constant for entire lifetime of the file, +* so if pre-content watches are added post execution or removed before the end +* of the execution, it will not cause i_writecount reference leak. +*/ +static inline int exe_file_deny_write_access (struct file *exe_file) +{ +if (unlikely (FMODE_FSNOTIFY_HSM (exe_file->f_mode))) +return 0 ; +return deny_write_access (exe_file); +} +static inline void exe_file_allow_write_access (struct file *exe_file) +{ +if (unlikely (!exe_file ||FMODE_FSNOTIFY_HSM (exe_file->f_mode))) +return ; +allow_write_access (exe_file); +} +static inline bool inode_is_open_for_write (const struct inode *inode) {return atomic_read (&inode->i_writecount) >0 ;
kernel/fork.c +6 -6
@@ -627,8 +627,8 @@ static void dup_mm_exe_file(struct mm_struct *mm, struct mm_struct *oldmm)* We depend on the oldmm having properly denied write access to the * exe_file already. */ -if (exe_file &&deny_write_access (exe_file)) -pr_warn_once ("deny_write_access() failed in %s\n" , __func__); +if (exe_file &&exe_file_deny_write_access (exe_file)) +pr_warn_once ("exe_file_deny_write_access() failed in %s\n" , __func__); }#ifdef CONFIG_MMU
@@ -1439,13 +1439,13 @@ int set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)* We expect the caller (i.e., sys_execve) to already denied * write access, so this is unlikely to fail. */ -if (unlikely (deny_write_access (new_exe_file))) +if (unlikely (exe_file_deny_write_access (new_exe_file)))return -EACCES ;get_file (new_exe_file); }rcu_assign_pointer (mm->exe_file, new_exe_file);if (old_exe_file) { -allow_write_access (old_exe_file); +exe_file_allow_write_access (old_exe_file);fput (old_exe_file); }return 0 ;
@@ -1486,7 +1486,7 @@ int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)return ret; } - ret =deny_write_access (new_exe_file); + ret =exe_file_deny_write_access (new_exe_file);if (ret)return -EACCES ;get_file (new_exe_file);
@@ -1498,7 +1498,7 @@ int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)mmap_write_unlock (mm);if (old_exe_file) { -allow_write_access (old_exe_file); +exe_file_allow_write_access (old_exe_file);fput (old_exe_file); }return 0 ;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 binfmt_misc: restore write access when removing an entry
In reply to Sasha LevinFrom: Christian Brauner <[email protected]>
[ Upstream commit db1856ea9196cf6e015d12199a34c0b9313c7bfa ]
Registering an entry with the MISC_FMT_OPEN_FILE flag opens the interpreter via open_exec() which denies write access to it for as long as the entry exists. Removing the entry closes the interpreter file via filp_close() but never restores write access, leaving the inode's i_writecount permanently negative. Opening the interpreter for writing keeps failing with ETXTBSY long after the entry is gone until the inode is evicted from the inode cache.
Commit 90f601b497d7 ("binfmt_misc: restore write access before closing files opened by open_exec()") fixed the same imbalance in the error path of bm_register_write() but the actual removal path has been leaking the write denial since the introduction of the flag.
Restore write access in put_binfmt_handler() before closing the interpreter file.
--- fs/binfmt_misc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/binfmt_misc.c +3 -1
@@ -161,8 +161,10 @@ static Node *get_binfmt_handler(struct binfmt_misc *misc,static void put_binfmt_handler (Node *e) {if (refcount_dec_and_test (&e->users)) { -if (e->flags &MISC_FMT_OPEN_FILE ) +if (e->flags &MISC_FMT_OPEN_FILE ) { +exe_file_allow_write_access (e->interp_file);filp_close (e->interp_file,NULL ); + }kfree (e); } }
Signature · 1 line
2.53.0