fs: don't block write during exec on pre-content watched files

[ Upstream commit 0357ef03c9 ]

Commit 2a010c4128 ("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.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Acked-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Jan Kara <jack@suse.cz>
Link: https://patch.msgid.link/20241128142532.465176-1-amir73il@gmail.com
Stable-dep-of: db1856ea91 ("binfmt_misc: restore write access when removing an entry")
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Amir Goldstein
2026-08-23 14:20:03 +02:00
committed by Greg Kroah-Hartman
parent 04958dba44
commit bf5ed2ef5c
5 changed files with 25 additions and 13 deletions
+2 -2
View File
@@ -1287,7 +1287,7 @@ out_free_interp:
}
reloc_func_desc = interp_load_addr;
allow_write_access(interpreter);
exe_file_allow_write_access(interpreter);
fput(interpreter);
kfree(interp_elf_ex);
@@ -1396,7 +1396,7 @@ out_free_dentry:
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:
+2 -2
View File
@@ -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);
+3 -3
View File
@@ -935,7 +935,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
path_noexec(&file->f_path))
goto exit;
err = deny_write_access(file);
err = exe_file_deny_write_access(file);
if (err)
goto exit;
@@ -1521,7 +1521,7 @@ static void free_bprm(struct linux_binprm *bprm)
abort_creds(bprm->cred);
}
if (bprm->file) {
allow_write_access(bprm->file);
exe_file_allow_write_access(bprm->file);
fput(bprm->file);
}
if (bprm->executable)
@@ -1824,7 +1824,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);
+12
View File
@@ -2824,6 +2824,18 @@ static inline void allow_write_access(struct file *file)
if (file)
atomic_inc(&file_inode(file)->i_writecount);
}
static inline int exe_file_deny_write_access(struct file *exe_file)
{
return deny_write_access(exe_file);
}
static inline void exe_file_allow_write_access(struct file *exe_file)
{
if (unlikely(!exe_file))
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;
+6 -6
View File
@@ -640,8 +640,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
@@ -1431,13 +1431,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;
@@ -1476,7 +1476,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);
@@ -1488,7 +1488,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;