Skip to content

Commit 0357ef0

Browse files
amir73iljankara
authored andcommitted
fs: don't block write during exec on pre-content watched files
Commit 2a010c4 ("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 <[email protected]> Acked-by: Christian Brauner <[email protected]> Signed-off-by: Jan Kara <[email protected]> Link: https://patch.msgid.link/[email protected]
1 parent 5121711 commit 0357ef0

File tree

5 files changed

+36
-14
lines changed

5 files changed

+36
-14
lines changed

fs/binfmt_elf.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,7 +1257,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
12571257
}
12581258
reloc_func_desc = interp_load_addr;
12591259

1260-
allow_write_access(interpreter);
1260+
exe_file_allow_write_access(interpreter);
12611261
fput(interpreter);
12621262

12631263
kfree(interp_elf_ex);
@@ -1354,7 +1354,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
13541354
kfree(interp_elf_ex);
13551355
kfree(interp_elf_phdata);
13561356
out_free_file:
1357-
allow_write_access(interpreter);
1357+
exe_file_allow_write_access(interpreter);
13581358
if (interpreter)
13591359
fput(interpreter);
13601360
out_free_ph:

fs/binfmt_elf_fdpic.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm)
394394
goto error;
395395
}
396396

397-
allow_write_access(interpreter);
397+
exe_file_allow_write_access(interpreter);
398398
fput(interpreter);
399399
interpreter = NULL;
400400
}
@@ -467,7 +467,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm)
467467

468468
error:
469469
if (interpreter) {
470-
allow_write_access(interpreter);
470+
exe_file_allow_write_access(interpreter);
471471
fput(interpreter);
472472
}
473473
kfree(interpreter_name);

fs/exec.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
912912
path_noexec(&file->f_path))
913913
return ERR_PTR(-EACCES);
914914

915-
err = deny_write_access(file);
915+
err = exe_file_deny_write_access(file);
916916
if (err)
917917
return ERR_PTR(err);
918918

@@ -927,7 +927,7 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
927927
* Returns ERR_PTR on failure or allocated struct file on success.
928928
*
929929
* As this is a wrapper for the internal do_open_execat(), callers
930-
* must call allow_write_access() before fput() on release. Also see
930+
* must call exe_file_allow_write_access() before fput() on release. Also see
931931
* do_close_execat().
932932
*/
933933
struct file *open_exec(const char *name)
@@ -1471,7 +1471,7 @@ static void do_close_execat(struct file *file)
14711471
{
14721472
if (!file)
14731473
return;
1474-
allow_write_access(file);
1474+
exe_file_allow_write_access(file);
14751475
fput(file);
14761476
}
14771477

@@ -1797,7 +1797,7 @@ static int exec_binprm(struct linux_binprm *bprm)
17971797
bprm->file = bprm->interpreter;
17981798
bprm->interpreter = NULL;
17991799

1800-
allow_write_access(exec);
1800+
exe_file_allow_write_access(exec);
18011801
if (unlikely(bprm->have_execfd)) {
18021802
if (bprm->executable) {
18031803
fput(exec);

include/linux/fs.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3095,6 +3095,28 @@ static inline void allow_write_access(struct file *file)
30953095
if (file)
30963096
atomic_inc(&file_inode(file)->i_writecount);
30973097
}
3098+
3099+
/*
3100+
* Do not prevent write to executable file when watched by pre-content events.
3101+
*
3102+
* Note that FMODE_FSNOTIFY_HSM mode is set depending on pre-content watches at
3103+
* the time of file open and remains constant for entire lifetime of the file,
3104+
* so if pre-content watches are added post execution or removed before the end
3105+
* of the execution, it will not cause i_writecount reference leak.
3106+
*/
3107+
static inline int exe_file_deny_write_access(struct file *exe_file)
3108+
{
3109+
if (unlikely(FMODE_FSNOTIFY_HSM(exe_file->f_mode)))
3110+
return 0;
3111+
return deny_write_access(exe_file);
3112+
}
3113+
static inline void exe_file_allow_write_access(struct file *exe_file)
3114+
{
3115+
if (unlikely(!exe_file || FMODE_FSNOTIFY_HSM(exe_file->f_mode)))
3116+
return;
3117+
allow_write_access(exe_file);
3118+
}
3119+
30983120
static inline bool inode_is_open_for_write(const struct inode *inode)
30993121
{
31003122
return atomic_read(&inode->i_writecount) > 0;

kernel/fork.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -625,8 +625,8 @@ static void dup_mm_exe_file(struct mm_struct *mm, struct mm_struct *oldmm)
625625
* We depend on the oldmm having properly denied write access to the
626626
* exe_file already.
627627
*/
628-
if (exe_file && deny_write_access(exe_file))
629-
pr_warn_once("deny_write_access() failed in %s\n", __func__);
628+
if (exe_file && exe_file_deny_write_access(exe_file))
629+
pr_warn_once("exe_file_deny_write_access() failed in %s\n", __func__);
630630
}
631631

632632
#ifdef CONFIG_MMU
@@ -1424,13 +1424,13 @@ int set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
14241424
* We expect the caller (i.e., sys_execve) to already denied
14251425
* write access, so this is unlikely to fail.
14261426
*/
1427-
if (unlikely(deny_write_access(new_exe_file)))
1427+
if (unlikely(exe_file_deny_write_access(new_exe_file)))
14281428
return -EACCES;
14291429
get_file(new_exe_file);
14301430
}
14311431
rcu_assign_pointer(mm->exe_file, new_exe_file);
14321432
if (old_exe_file) {
1433-
allow_write_access(old_exe_file);
1433+
exe_file_allow_write_access(old_exe_file);
14341434
fput(old_exe_file);
14351435
}
14361436
return 0;
@@ -1471,7 +1471,7 @@ int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
14711471
return ret;
14721472
}
14731473

1474-
ret = deny_write_access(new_exe_file);
1474+
ret = exe_file_deny_write_access(new_exe_file);
14751475
if (ret)
14761476
return -EACCES;
14771477
get_file(new_exe_file);
@@ -1483,7 +1483,7 @@ int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)
14831483
mmap_write_unlock(mm);
14841484

14851485
if (old_exe_file) {
1486-
allow_write_access(old_exe_file);
1486+
exe_file_allow_write_access(old_exe_file);
14871487
fput(old_exe_file);
14881488
}
14891489
return 0;

0 commit comments

Comments
 (0)