This useful behaviour is implemented for most filesystems, and wants to be implemented for every filesystem, quoth ref: There is general agreement that we should standardize all file systems to prevent modifications even for files that were opened at the time the immutable flag is set. Eventually, a change to enforce this at the VFS layer should be landing in mainline. References: commit 02b016ca7f99 ("ext4: enforce the immutable flag on open files") Signed-off-by: Ahelenia ZiemiaƄska --- /ext4# uname -a Linux tarta 6.18.0-10912-g416f99c3b16f-dirty #1 SMP PREEMPT_DYNAMIC Sat Dec 6 12:14:41 CET 2025 x86_64 GNU/Linux /ext4# while sleep 1; do echo $$; done > file & [1] 262 /ext4# chattr +i file /ext4# sh: line 25: echo: write error: Operation not permitted sh: line 25: echo: write error: Operation not permitted sh: line 25: echo: write error: Operation not permitted sh: line 25: echo: write error: Operation not permitted fg while sleep 1; do echo $$; done > file ^C /ext4# mount -t tmpfs tmpfs /tmp /ext4# cd /tmp /tmp# while sleep 1; do echo $$; done > file & [1] 284 /tmp# chattr +i file /tmp# sh: line 35: echo: write error: Operation not permitted sh: line 35: echo: write error: Operation not permitted sh: line 35: echo: write error: Operation not permitted mm/filemap.c | 10 ++++++++-- mm/shmem.c | 12 ++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/mm/filemap.c b/mm/filemap.c index ebd75684cb0a..0b0d5cfbcd44 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3945,12 +3945,18 @@ EXPORT_SYMBOL(filemap_map_pages); vm_fault_t filemap_page_mkwrite(struct vm_fault *vmf) { - struct address_space *mapping = vmf->vma->vm_file->f_mapping; + struct file *file = vmf->vma->vm_file; + struct address_space *mapping = file->f_mapping; struct folio *folio = page_folio(vmf->page); vm_fault_t ret = VM_FAULT_LOCKED; + if (unlikely(IS_IMMUTABLE(file_inode(file)))) { + ret = VM_FAULT_SIGBUS; + goto out; + } + sb_start_pagefault(mapping->host->i_sb); - file_update_time(vmf->vma->vm_file); + file_update_time(file); folio_lock(folio); if (folio->mapping != mapping) { folio_unlock(folio); diff --git a/mm/shmem.c b/mm/shmem.c index d578d8e765d7..5d3fbf4efb3d 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1294,6 +1294,14 @@ static int shmem_setattr(struct mnt_idmap *idmap, bool update_mtime = false; bool update_ctime = true; + if (unlikely(IS_IMMUTABLE(inode))) + return -EPERM; + + if (unlikely(IS_APPEND(inode) && + (attr->ia_valid & (ATTR_MODE | ATTR_UID | + ATTR_GID | ATTR_TIMES_SET)))) + return -EPERM; + error = setattr_prepare(idmap, dentry, attr); if (error) return error; @@ -3475,6 +3483,10 @@ static ssize_t shmem_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ret = generic_write_checks(iocb, from); if (ret <= 0) goto unlock; + if (unlikely(IS_IMMUTABLE(inode))) { + ret = -EPERM; + goto unlock; + } ret = file_remove_privs(file); if (ret) goto unlock; -- 2.39.5