Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C902046D091; Thu, 20 Aug 2026 16:37:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787243859; cv=none; b=EyGpiITamuHk0iPHxsiVquDPrSv+sompXlTorPG284nK1ZAFShhuP1ZujhcS40gwgAMmI90qbd9NcAkbxKJ5UHV1vuYnA46lO7Bs/w2Ux5rv7HsdIxahBF0tMTyPoaawSH/1slR2OJLcTO3uL5diESOF7iWauVewZWI1KhrCwmE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787243859; c=relaxed/simple; bh=CSp5pWGfnsKn/tpHddqFD8abVFewqxQ8RKJKl1OyeHw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SqbZtCuWzI769te6zVHWXetW5W56ebVbX9pJr1ILctvork6B2DmeBoMal0mH2ex/BGEnE4wLiwD+kR99TJKQr04IIfGGV03WoZhhbkHD5yTzfe6+P8u4bLci8pq5tode8JeJ2+mJ7FXb8PKGauAw9Sb4pTV/sx9po3AEkX6z+4A= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=WEZwRYyH; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="WEZwRYyH" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 25D851F000E9; Thu, 20 Aug 2026 16:37:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1787243857; bh=jNv2hBnAzH8Y8I4JG2BgcVy4d64lFXvdz43emiCxiPs=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=WEZwRYyHRIXG2wxkFbySqgMLw4o1dEgT0zZByfiWG5coM6GwPKH4GvSWRLwprcSsP VXgKGi57zZ6A54NA2hJ/OidzCoPtDOm1M6gjrga6Acygb6y7jlSYATaimm8bbGBTKr 5bj8BpnVzObwm2U71HO/XhiduJr1bkhm6wIJopwQ= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Amir Goldstein , Christian Brauner , Jan Kara , Sasha Levin Subject: [PATCH 5.15 238/272] fs: dont block write during exec on pre-content watched files Date: Thu, 20 Aug 2026 16:57:02 +0200 Message-ID: <20260820145238.756055456@linuxfoundation.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260820145231.229664293@linuxfoundation.org> References: <20260820145231.229664293@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 5.15-stable review patch. If anyone has any objections, please let me know. ------------------ From: Amir Goldstein [ 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. Signed-off-by: Amir Goldstein Acked-by: Christian Brauner Signed-off-by: Jan Kara Link: https://patch.msgid.link/20241128142532.465176-1-amir73il@gmail.com Stable-dep-of: db1856ea9196 ("binfmt_misc: restore write access when removing an entry") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/binfmt_elf.c | 4 ++-- fs/binfmt_elf_fdpic.c | 4 ++-- fs/exec.c | 6 +++--- include/linux/fs.h | 22 ++++++++++++++++++++++ kernel/fork.c | 12 ++++++------ 5 files changed, 35 insertions(+), 13 deletions(-) --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1243,7 +1243,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); @@ -1332,7 +1332,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: --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -398,7 +398,7 @@ static int load_elf_fdpic_binary(struct 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 error: if (interpreter) { - allow_write_access(interpreter); + exe_file_allow_write_access(interpreter); fput(interpreter); } kfree(interpreter_name); --- a/fs/exec.c +++ b/fs/exec.c @@ -929,7 +929,7 @@ static struct file *do_open_execat(int f path_noexec(&file->f_path)) goto exit; - err = deny_write_access(file); + err = exe_file_deny_write_access(file); if (err) goto exit; @@ -1499,7 +1499,7 @@ static void free_bprm(struct linux_binpr 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) @@ -1790,7 +1790,7 @@ static int exec_binprm(struct linux_binp 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); --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3143,6 +3143,28 @@ static inline void allow_write_access(st 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; --- a/kernel/fork.c +++ b/kernel/fork.c @@ -487,8 +487,8 @@ static void dup_mm_exe_file(struct mm_st * 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 @@ -1201,13 +1201,13 @@ int set_mm_exe_file(struct mm_struct *mm * 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; @@ -1246,7 +1246,7 @@ int replace_mm_exe_file(struct mm_struct } /* set the new file, lockless */ - 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); @@ -1258,7 +1258,7 @@ int replace_mm_exe_file(struct mm_struct * write access while someone might open the file writable. */ mmap_read_lock(mm); - allow_write_access(old_exe_file); + exe_file_allow_write_access(old_exe_file); fput(old_exe_file); mmap_read_unlock(mm); }