From f08fe8891c3eeb63b73f9f1f6d97aa629c821579 Mon Sep 17 00:00:00 2001 From: Zhihao Cheng Date: Fri, 30 Jan 2026 11:48:53 +0800 Subject: [PATCH 01/28] dcache: Limit the minimal number of bucket to two There is an OOB read problem on dentry_hashtable when user sets 'dhash_entries=1': BUG: unable to handle page fault for address: ffff888b30b774b0 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page Oops: Oops: 0000 [#1] SMP PTI RIP: 0010:__d_lookup+0x56/0x120 Call Trace: d_lookup.cold+0x16/0x5d lookup_dcache+0x27/0xf0 lookup_one_qstr_excl+0x2a/0x180 start_dirop+0x55/0xa0 simple_start_creating+0x8d/0xa0 debugfs_start_creating+0x8c/0x180 debugfs_create_dir+0x1d/0x1c0 pinctrl_init+0x6d/0x140 do_one_initcall+0x6d/0x3d0 kernel_init_freeable+0x39f/0x460 kernel_init+0x2a/0x260 There will be only one bucket in dentry_hashtable when dhash_entries is set as one, and d_hash_shift is calculated as 32 by dcache_init(). Then, following process will access more than one buckets(which memory region is not allocated) in dentry_hashtable: d_lookup b = d_hash(hash) dentry_hashtable + ((u32)hashlen >> d_hash_shift) // The C standard defines the behavior of right shift amounts // exceeding the bit width of the operand as undefined. The // result of '(u32)hashlen >> d_hash_shift' becomes 'hashlen', // so 'b' will point to an unallocated memory region. hlist_bl_for_each_entry_rcu(b) hlist_bl_first_rcu(head) h->first // read OOB! Fix it by limiting the minimal number of dentry_hashtable bucket to two, so that 'd_hash_shift' won't exceeds the bit width of type u32. Cc: stable@vger.kernel.org Signed-off-by: Zhihao Cheng Link: https://patch.msgid.link/20260130034853.215819-1-chengzhihao1@huawei.com Reviewed-by: Yang Erkun Signed-off-by: Christian Brauner --- fs/dcache.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 7ba1801d8132..24f4f3acaa8c 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -3257,7 +3257,7 @@ static void __init dcache_init_early(void) HASH_EARLY | HASH_ZERO, &d_hash_shift, NULL, - 0, + 2, 0); d_hash_shift = 32 - d_hash_shift; @@ -3289,7 +3289,7 @@ static void __init dcache_init(void) HASH_ZERO, &d_hash_shift, NULL, - 0, + 2, 0); d_hash_shift = 32 - d_hash_shift; From 0d799df5b147e08828887fe7299efd7a9e0eea40 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 19 Feb 2026 07:50:01 +0100 Subject: [PATCH 02/28] fs: mark bool_names static The bool_names array is only used in fs_parser.c so mark it static. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260219065014.3550402-2-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/fs_parser.c | 3 +-- include/linux/fs_parser.h | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/fs_parser.c b/fs/fs_parser.c index c092a9f79e32..46993e31137d 100644 --- a/fs/fs_parser.c +++ b/fs/fs_parser.c @@ -13,7 +13,7 @@ #include #include "internal.h" -const struct constant_table bool_names[] = { +static const struct constant_table bool_names[] = { { "0", false }, { "1", true }, { "false", false }, @@ -22,7 +22,6 @@ const struct constant_table bool_names[] = { { "yes", true }, { }, }; -EXPORT_SYMBOL(bool_names); static const struct constant_table * __lookup_constant(const struct constant_table *tbl, const char *name) diff --git a/include/linux/fs_parser.h b/include/linux/fs_parser.h index 5e8a3b546033..ac8253cca2bc 100644 --- a/include/linux/fs_parser.h +++ b/include/linux/fs_parser.h @@ -84,8 +84,6 @@ extern int fs_lookup_param(struct fs_context *fc, extern int lookup_constant(const struct constant_table tbl[], const char *name, int not_found); -extern const struct constant_table bool_names[]; - #ifdef CONFIG_VALIDATE_FS_PARSER extern bool fs_validate_description(const char *name, const struct fs_parameter_spec *desc); From 8823db29744fceda9f94e674f74deea446c620b3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 19 Feb 2026 07:50:02 +0100 Subject: [PATCH 03/28] fs: remove fsparam_blob / fs_param_is_blob These are not used anywhere even after the fs_context conversion is finished, so remove them. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260219065014.3550402-3-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- Documentation/filesystems/mount_api.rst | 2 -- fs/fs_parser.c | 9 --------- include/linux/fs_parser.h | 3 +-- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/Documentation/filesystems/mount_api.rst b/Documentation/filesystems/mount_api.rst index a064234fed5b..b4a0f23914a6 100644 --- a/Documentation/filesystems/mount_api.rst +++ b/Documentation/filesystems/mount_api.rst @@ -647,7 +647,6 @@ The members are as follows: fs_param_is_u64 64-bit unsigned int result->uint_64 fs_param_is_enum Enum value name result->uint_32 fs_param_is_string Arbitrary string param->string - fs_param_is_blob Binary blob param->blob fs_param_is_blockdev Blockdev path * Needs lookup fs_param_is_path Path * Needs lookup fs_param_is_fd File descriptor result->int_32 @@ -681,7 +680,6 @@ The members are as follows: fsparam_u64() fs_param_is_u64 fsparam_enum() fs_param_is_enum fsparam_string() fs_param_is_string - fsparam_blob() fs_param_is_blob fsparam_bdev() fs_param_is_blockdev fsparam_path() fs_param_is_path fsparam_fd() fs_param_is_fd diff --git a/fs/fs_parser.c b/fs/fs_parser.c index 46993e31137d..79e8fe9176fa 100644 --- a/fs/fs_parser.c +++ b/fs/fs_parser.c @@ -277,15 +277,6 @@ int fs_param_is_string(struct p_log *log, const struct fs_parameter_spec *p, } EXPORT_SYMBOL(fs_param_is_string); -int fs_param_is_blob(struct p_log *log, const struct fs_parameter_spec *p, - struct fs_parameter *param, struct fs_parse_result *result) -{ - if (param->type != fs_value_is_blob) - return fs_param_bad_value(log, param); - return 0; -} -EXPORT_SYMBOL(fs_param_is_blob); - int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { diff --git a/include/linux/fs_parser.h b/include/linux/fs_parser.h index ac8253cca2bc..961562b101c5 100644 --- a/include/linux/fs_parser.h +++ b/include/linux/fs_parser.h @@ -27,7 +27,7 @@ typedef int fs_param_type(struct p_log *, * The type of parameter expected. */ fs_param_type fs_param_is_bool, fs_param_is_u32, fs_param_is_s32, fs_param_is_u64, - fs_param_is_enum, fs_param_is_string, fs_param_is_blob, fs_param_is_blockdev, + fs_param_is_enum, fs_param_is_string, fs_param_is_blockdev, fs_param_is_path, fs_param_is_fd, fs_param_is_uid, fs_param_is_gid, fs_param_is_file_or_string; @@ -125,7 +125,6 @@ static inline bool fs_validate_description(const char *name, #define fsparam_enum(NAME, OPT, array) __fsparam(fs_param_is_enum, NAME, OPT, 0, array) #define fsparam_string(NAME, OPT) \ __fsparam(fs_param_is_string, NAME, OPT, 0, NULL) -#define fsparam_blob(NAME, OPT) __fsparam(fs_param_is_blob, NAME, OPT, 0, NULL) #define fsparam_bdev(NAME, OPT) __fsparam(fs_param_is_blockdev, NAME, OPT, 0, NULL) #define fsparam_path(NAME, OPT) __fsparam(fs_param_is_path, NAME, OPT, 0, NULL) #define fsparam_fd(NAME, OPT) __fsparam(fs_param_is_fd, NAME, OPT, 0, NULL) From d2f2f7cf8e898f7b80fe031a263df9c7de94b0b7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 19 Feb 2026 07:50:03 +0100 Subject: [PATCH 04/28] fs: remove fsparam_path / fs_param_is_path These are not used anywhere even after the fs_context conversion is finished, so remove them. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260219065014.3550402-4-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- Documentation/filesystems/mount_api.rst | 2 -- fs/fs_parser.c | 7 ------- include/linux/fs_parser.h | 3 +-- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/Documentation/filesystems/mount_api.rst b/Documentation/filesystems/mount_api.rst index b4a0f23914a6..e8b94357b4df 100644 --- a/Documentation/filesystems/mount_api.rst +++ b/Documentation/filesystems/mount_api.rst @@ -648,7 +648,6 @@ The members are as follows: fs_param_is_enum Enum value name result->uint_32 fs_param_is_string Arbitrary string param->string fs_param_is_blockdev Blockdev path * Needs lookup - fs_param_is_path Path * Needs lookup fs_param_is_fd File descriptor result->int_32 fs_param_is_uid User ID (u32) result->uid fs_param_is_gid Group ID (u32) result->gid @@ -681,7 +680,6 @@ The members are as follows: fsparam_enum() fs_param_is_enum fsparam_string() fs_param_is_string fsparam_bdev() fs_param_is_blockdev - fsparam_path() fs_param_is_path fsparam_fd() fs_param_is_fd fsparam_uid() fs_param_is_uid fsparam_gid() fs_param_is_gid diff --git a/fs/fs_parser.c b/fs/fs_parser.c index 79e8fe9176fa..b4cc4cce518a 100644 --- a/fs/fs_parser.c +++ b/fs/fs_parser.c @@ -361,13 +361,6 @@ int fs_param_is_blockdev(struct p_log *log, const struct fs_parameter_spec *p, } EXPORT_SYMBOL(fs_param_is_blockdev); -int fs_param_is_path(struct p_log *log, const struct fs_parameter_spec *p, - struct fs_parameter *param, struct fs_parse_result *result) -{ - return 0; -} -EXPORT_SYMBOL(fs_param_is_path); - #ifdef CONFIG_VALIDATE_FS_PARSER /** * fs_validate_description - Validate a parameter specification array diff --git a/include/linux/fs_parser.h b/include/linux/fs_parser.h index 961562b101c5..98b83708f92b 100644 --- a/include/linux/fs_parser.h +++ b/include/linux/fs_parser.h @@ -28,7 +28,7 @@ typedef int fs_param_type(struct p_log *, */ fs_param_type fs_param_is_bool, fs_param_is_u32, fs_param_is_s32, fs_param_is_u64, fs_param_is_enum, fs_param_is_string, fs_param_is_blockdev, - fs_param_is_path, fs_param_is_fd, fs_param_is_uid, fs_param_is_gid, + fs_param_is_fd, fs_param_is_uid, fs_param_is_gid, fs_param_is_file_or_string; /* @@ -126,7 +126,6 @@ static inline bool fs_validate_description(const char *name, #define fsparam_string(NAME, OPT) \ __fsparam(fs_param_is_string, NAME, OPT, 0, NULL) #define fsparam_bdev(NAME, OPT) __fsparam(fs_param_is_blockdev, NAME, OPT, 0, NULL) -#define fsparam_path(NAME, OPT) __fsparam(fs_param_is_path, NAME, OPT, 0, NULL) #define fsparam_fd(NAME, OPT) __fsparam(fs_param_is_fd, NAME, OPT, 0, NULL) #define fsparam_file_or_string(NAME, OPT) \ __fsparam(fs_param_is_file_or_string, NAME, OPT, 0, NULL) From bc014937bc112d4d44b3f3186fb8e87a3c80f735 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 19 Feb 2026 07:50:04 +0100 Subject: [PATCH 05/28] fs: unexport fs_context_for_reconfigure fs_context_for_reconfigure is only used by core VFS code and devtmpfs, so unexport it. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260219065014.3550402-5-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/fs_context.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/fs_context.c b/fs/fs_context.c index 81ed94f46cac..4a7b8fde4c5c 100644 --- a/fs/fs_context.c +++ b/fs/fs_context.c @@ -318,7 +318,6 @@ struct fs_context *fs_context_for_reconfigure(struct dentry *dentry, return alloc_fs_context(dentry->d_sb->s_type, dentry, sb_flags, sb_flags_mask, FS_CONTEXT_FOR_RECONFIGURE); } -EXPORT_SYMBOL(fs_context_for_reconfigure); /** * fs_context_for_submount: allocate a new fs_context for a submount From f7df26875b9a07d756a5f4cac26dac95bf1d9de1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 27 Feb 2026 15:20:35 +0000 Subject: [PATCH 06/28] selftests/filesystems: Assume that TIOCGPTPEER is defined The devpts_pts selftest has an ifdef in case an architecture does not define TIOCGPTPEER, but the handling for this is broken since we need errno to be set to EINVAL in order to skip the test as we should. Given that this ioctl() has been defined since v4.15 we may as well just assume it's there rather than write handling code which will probably never get used. Signed-off-by: Mark Brown Link: https://patch.msgid.link/20260227-selftests-filesystems-devpts-tiocgptpeer-v3-1-07db4d85d5aa@kernel.org Signed-off-by: Christian Brauner --- tools/testing/selftests/filesystems/devpts_pts.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/testing/selftests/filesystems/devpts_pts.c b/tools/testing/selftests/filesystems/devpts_pts.c index 54fea349204e..aa8d5324f2a6 100644 --- a/tools/testing/selftests/filesystems/devpts_pts.c +++ b/tools/testing/selftests/filesystems/devpts_pts.c @@ -119,9 +119,7 @@ static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents) goto do_cleanup; } -#ifdef TIOCGPTPEER slave = ioctl(master, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC); -#endif if (slave < 0) { if (errno == EINVAL) { fprintf(stderr, "TIOCGPTPEER is not supported. " From 2727d44f5d5bc3f8e55a6a0ccf24d8105a5a400e Mon Sep 17 00:00:00 2001 From: Kit Dallege Date: Sun, 15 Mar 2026 18:09:31 +0100 Subject: [PATCH 07/28] writeback: fix kernel-doc function name mismatch for wb_put_many() The kernel-doc comment says wb_put but the actual function is wb_put_many. Fix the name to match. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Kit Dallege Link: https://patch.msgid.link/20260315170931.65852-1-xaum.io@gmail.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- include/linux/backing-dev-defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/backing-dev-defs.h b/include/linux/backing-dev-defs.h index c88fd4d37d1f..a06b93446d10 100644 --- a/include/linux/backing-dev-defs.h +++ b/include/linux/backing-dev-defs.h @@ -237,7 +237,7 @@ static inline void wb_get(struct bdi_writeback *wb) } /** - * wb_put - decrement a wb's refcount + * wb_put_many - decrement a wb's refcount * @wb: bdi_writeback to put * @nr: number of references to put */ From 0621c385fda1376e967f37ccd534c26c3e511d14 Mon Sep 17 00:00:00 2001 From: HyungJung Joo Date: Tue, 17 Mar 2026 14:48:27 +0900 Subject: [PATCH 08/28] fs/omfs: reject s_sys_blocksize smaller than OMFS_DIR_START omfs_fill_super() rejects oversized s_sys_blocksize values (> PAGE_SIZE), but it does not reject values smaller than OMFS_DIR_START (0x1b8 = 440). Later, omfs_make_empty() uses sbi->s_sys_blocksize - OMFS_DIR_START as the length argument to memset(). Since s_sys_blocksize is u32, a crafted filesystem image with s_sys_blocksize < OMFS_DIR_START causes an unsigned underflow there, wrapping to a value near 2^32. That drives a ~4 GiB memset() from bh->b_data + OMFS_DIR_START and overwrites kernel memory far beyond the backing block buffer. Add the corresponding lower-bound check alongside the existing upper-bound check in omfs_fill_super(), so that malformed images are rejected during superblock validation before any filesystem data is processed. Fixes: a3ab7155ea21 ("omfs: add directory routines") Signed-off-by: Hyungjung Joo Link: https://patch.msgid.link/20260317054827.1822061-1-jhj140711@gmail.com Signed-off-by: Christian Brauner --- fs/omfs/inode.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index 701ed85d9831..23aa3f54aaba 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -513,6 +513,12 @@ static int omfs_fill_super(struct super_block *sb, struct fs_context *fc) goto out_brelse_bh; } + if (sbi->s_sys_blocksize < OMFS_DIR_START) { + printk(KERN_ERR "omfs: sysblock size (%d) is too small\n", + sbi->s_sys_blocksize); + goto out_brelse_bh; + } + if (sbi->s_blocksize < sbi->s_sys_blocksize || sbi->s_blocksize > OMFS_MAX_BLOCK_SIZE) { printk(KERN_ERR "omfs: block size (%d) is out of range\n", From d227786ab1119669df4dc333a61510c52047cce4 Mon Sep 17 00:00:00 2001 From: HyungJung Joo Date: Tue, 17 Mar 2026 14:45:56 +0900 Subject: [PATCH 09/28] fs/mbcache: cancel shrink work before destroying the cache mb_cache_destroy() calls shrinker_free() and then frees all cache entries and the cache itself, but it does not cancel the pending c_shrink_work work item first. If mb_cache_entry_create() schedules c_shrink_work via schedule_work() and the work item is still pending or running when mb_cache_destroy() runs, mb_cache_shrink_worker() will access the cache after its memory has been freed, causing a use-after-free. This is only reachable by a privileged user (root or CAP_SYS_ADMIN) who can trigger the last put of a mounted ext2/ext4/ocfs2 filesystem. Cancel the work item with cancel_work_sync() before calling shrinker_free(), ensuring the worker has finished and will not be rescheduled before the cache is torn down. Fixes: c2f3140fe2ec ("mbcache2: limit cache size") Signed-off-by: Hyungjung Joo Link: https://patch.msgid.link/20260317054556.1821600-1-jhj140711@gmail.com Signed-off-by: Christian Brauner --- fs/mbcache.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/mbcache.c b/fs/mbcache.c index e60a840999aa..90b0564c62d0 100644 --- a/fs/mbcache.c +++ b/fs/mbcache.c @@ -408,6 +408,7 @@ void mb_cache_destroy(struct mb_cache *cache) { struct mb_cache_entry *entry, *next; + cancel_work_sync(&cache->c_shrink_work); shrinker_free(cache->c_shrink); /* From f8909447894aea7660c84c9d599ffd6b9a3772da Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 18 Mar 2026 01:12:21 +0100 Subject: [PATCH 10/28] autofs: replace manual symlink buffer allocation in autofs_dir_symlink The symlink name was previously duplicated using an explicit kmalloc() followed by strcpy(), which is deprecated [1]. Replace this open-coded string duplication with kstrdup(), which allocates and copies the symlink name with a single helper function. Remove the local variable 'size' and set 'i_size' directly using strlen(cp), which is equivalent to the previous value of 'size'. This simplifies the code, uses common string-handling helpers, and removes the deprecated use of strcpy(). Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strcpy [1] Acked-by: Ian Kent Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260318001219.2354-3-thorsten.blum@linux.dev Signed-off-by: Christian Brauner --- fs/autofs/root.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 2c31002b314a..186e960f1e23 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -7,6 +7,7 @@ #include #include +#include #include "autofs_i.h" @@ -578,7 +579,6 @@ static int autofs_dir_symlink(struct mnt_idmap *idmap, struct autofs_info *ino = autofs_dentry_ino(dentry); struct autofs_info *p_ino; struct inode *inode; - size_t size = strlen(symname); char *cp; pr_debug("%s <- %pd\n", symname, dentry); @@ -589,19 +589,17 @@ static int autofs_dir_symlink(struct mnt_idmap *idmap, autofs_del_active(dentry); - cp = kmalloc(size + 1, GFP_KERNEL); + cp = kstrdup(symname, GFP_KERNEL); if (!cp) return -ENOMEM; - strcpy(cp, symname); - inode = autofs_get_inode(dir->i_sb, S_IFLNK | 0555); if (!inode) { kfree(cp); return -ENOMEM; } inode->i_private = cp; - inode->i_size = size; + inode->i_size = strlen(cp); d_make_persistent(dentry, inode); p_ino = autofs_dentry_ino(dentry->d_parent); From 49e64d7136366a9657d581158d4a20399231530c Mon Sep 17 00:00:00 2001 From: Askar Safin Date: Thu, 19 Mar 2026 23:27:21 +0000 Subject: [PATCH 11/28] init/initramfs.c: trivial fix: FSM -> Finite-state machine FSM means "finite-state machine", but I think this is not obvious to everyone. Signed-off-by: Askar Safin Link: https://patch.msgid.link/20260319232721.452950-1-safinaskar@gmail.com Signed-off-by: Christian Brauner --- init/initramfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/initramfs.c b/init/initramfs.c index 6ddbfb17fb8f..24d7cde4fff0 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -212,7 +212,7 @@ static void __init parse_header(char *s) hdr_csum = parsed[12]; } -/* FSM */ +/* Finite-state machine */ static __initdata enum state { Start, From 3d6bb84f6bb3f4c05fc47fd02ce75ce3032a4ce1 Mon Sep 17 00:00:00 2001 From: Yuto Ohnuki Date: Thu, 26 Feb 2026 20:18:58 +0000 Subject: [PATCH 12/28] fs: remove stale and duplicate forward declarations Remove the following unnecessary forward declarations from fs.h, which improves maintainability. - struct hd_geometry: became unused in fs.h when block_device_operations was moved to blkdev.h in commit 08f858512151 ("[PATCH] move block_device_operations to blkdev.h"). The forward declaration is now added to blkdev.h where it is actually used. - struct iovec: became unused when aio_read/aio_write were removed in commit 8436318205b9 ("->aio_read and ->aio_write removed") - struct iov_iter: duplicate forward declaration. This removes the redundant second declaration, added in commit 293bc9822fa9 ("new methods: ->read_iter() and ->write_iter()") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202512301303.s7YWTZHA-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202512302139.Wl0soAlz-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202512302105.pmzYfmcV-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202512302125.FNgHwu5z-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202512302108.nIV8r5ES-lkp@intel.com/ Signed-off-by: Yuto Ohnuki Link: https://patch.msgid.link/20260226201857.27310-2-ytohnuki@amazon.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- include/linux/blkdev.h | 1 + include/linux/fs.h | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 99ef8cd7673c..54cdd71aab07 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -38,6 +38,7 @@ struct blk_flush_queue; struct kiocb; struct pr_ops; struct rq_qos; +struct hd_geometry; struct blk_report_zones_args; struct blk_queue_stats; struct blk_stat_callback; diff --git a/include/linux/fs.h b/include/linux/fs.h index a2af5ddd5323..280d43c9f04a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -55,8 +55,6 @@ struct bdi_writeback; struct bio; struct io_comp_batch; struct fiemap_extent_info; -struct hd_geometry; -struct iovec; struct kiocb; struct kobject; struct pipe_inode_info; @@ -1917,7 +1915,6 @@ struct dir_context { */ #define COPY_FILE_SPLICE (1 << 0) -struct iov_iter; struct io_uring_cmd; struct offset_ctx; From e43dce8a0bc09083ea1145a1a0c61d83cbe72d97 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Mar 2026 08:01:44 +0100 Subject: [PATCH 13/28] fs: fix archiecture-specific compat_ftruncate64 The "small" argument to do_sys_ftruncate indicates if > 32-bit size should be reject, but all the arch-specific compat ftruncate64 implementations get this wrong. Merge do_sys_ftruncate and ksys_ftruncate, replace the integer as boolean small flag with a descriptive one about LFS semantics, and use it correctly in the architecture-specific ftruncate64 implementations. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Fixes: 3dd681d944f6 ("arm64: 32-bit (compat) applications support") Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260323070205.2939118-2-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- arch/arm64/kernel/sys32.c | 2 +- arch/mips/kernel/linux32.c | 2 +- arch/parisc/kernel/sys_parisc.c | 4 ++-- arch/powerpc/kernel/sys_ppc32.c | 2 +- arch/sparc/kernel/sys_sparc32.c | 2 +- arch/x86/kernel/sys_ia32.c | 3 ++- fs/internal.h | 1 - fs/open.c | 12 ++++++------ include/linux/syscalls.h | 8 ++------ 9 files changed, 16 insertions(+), 20 deletions(-) diff --git a/arch/arm64/kernel/sys32.c b/arch/arm64/kernel/sys32.c index 96bcfb907443..12a948f3a504 100644 --- a/arch/arm64/kernel/sys32.c +++ b/arch/arm64/kernel/sys32.c @@ -89,7 +89,7 @@ COMPAT_SYSCALL_DEFINE4(aarch32_truncate64, const char __user *, pathname, COMPAT_SYSCALL_DEFINE4(aarch32_ftruncate64, unsigned int, fd, u32, __pad, arg_u32p(length)) { - return ksys_ftruncate(fd, arg_u64(length)); + return ksys_ftruncate(fd, arg_u64(length), FTRUNCATE_LFS); } COMPAT_SYSCALL_DEFINE5(aarch32_readahead, int, fd, u32, __pad, diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index a0c0a7a654e9..fe9a787db569 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -60,7 +60,7 @@ SYSCALL_DEFINE4(32_truncate64, const char __user *, path, SYSCALL_DEFINE4(32_ftruncate64, unsigned long, fd, unsigned long, __dummy, unsigned long, a2, unsigned long, a3) { - return ksys_ftruncate(fd, merge_64(a2, a3)); + return ksys_ftruncate(fd, merge_64(a2, a3), FTRUNCATE_LFS); } SYSCALL_DEFINE5(32_llseek, unsigned int, fd, unsigned int, offset_high, diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c index b2cdbb8a12b1..fcb0d8069139 100644 --- a/arch/parisc/kernel/sys_parisc.c +++ b/arch/parisc/kernel/sys_parisc.c @@ -216,7 +216,7 @@ asmlinkage long parisc_truncate64(const char __user * path, asmlinkage long parisc_ftruncate64(unsigned int fd, unsigned int high, unsigned int low) { - return ksys_ftruncate(fd, (long)high << 32 | low); + return ksys_ftruncate(fd, (long)high << 32 | low, FTRUNCATE_LFS); } /* stubs for the benefit of the syscall_table since truncate64 and truncate @@ -227,7 +227,7 @@ asmlinkage long sys_truncate64(const char __user * path, unsigned long length) } asmlinkage long sys_ftruncate64(unsigned int fd, unsigned long length) { - return ksys_ftruncate(fd, length); + return ksys_ftruncate(fd, length, FTRUNCATE_LFS); } asmlinkage long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) { diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c index d451a8229223..03fa487f2614 100644 --- a/arch/powerpc/kernel/sys_ppc32.c +++ b/arch/powerpc/kernel/sys_ppc32.c @@ -101,7 +101,7 @@ PPC32_SYSCALL_DEFINE4(ppc_ftruncate64, unsigned int, fd, u32, reg4, unsigned long, len1, unsigned long, len2) { - return ksys_ftruncate(fd, merge_64(len1, len2)); + return ksys_ftruncate(fd, merge_64(len1, len2), FTRUNCATE_LFS); } PPC32_SYSCALL_DEFINE6(ppc32_fadvise64, diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c index f84a02ab6bf9..04432b82b9e3 100644 --- a/arch/sparc/kernel/sys_sparc32.c +++ b/arch/sparc/kernel/sys_sparc32.c @@ -58,7 +58,7 @@ COMPAT_SYSCALL_DEFINE3(truncate64, const char __user *, path, u32, high, u32, lo COMPAT_SYSCALL_DEFINE3(ftruncate64, unsigned int, fd, u32, high, u32, low) { - return ksys_ftruncate(fd, ((u64)high << 32) | low); + return ksys_ftruncate(fd, ((u64)high << 32) | low, FTRUNCATE_LFS); } static int cp_compat_stat64(struct kstat *stat, diff --git a/arch/x86/kernel/sys_ia32.c b/arch/x86/kernel/sys_ia32.c index 6cf65397d225..610a1c2f4519 100644 --- a/arch/x86/kernel/sys_ia32.c +++ b/arch/x86/kernel/sys_ia32.c @@ -61,7 +61,8 @@ SYSCALL_DEFINE3(ia32_truncate64, const char __user *, filename, SYSCALL_DEFINE3(ia32_ftruncate64, unsigned int, fd, unsigned long, offset_low, unsigned long, offset_high) { - return ksys_ftruncate(fd, ((loff_t) offset_high << 32) | offset_low); + return ksys_ftruncate(fd, ((loff_t) offset_high << 32) | offset_low, + FTRUNCATE_LFS); } /* warning: next two assume little endian */ diff --git a/fs/internal.h b/fs/internal.h index cbc384a1aa09..2663823e273a 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -199,7 +199,6 @@ extern int build_open_flags(const struct open_how *how, struct open_flags *op); struct file *file_close_fd_locked(struct files_struct *files, unsigned fd); int do_ftruncate(struct file *file, loff_t length, int small); -int do_sys_ftruncate(unsigned int fd, loff_t length, int small); int chmod_common(const struct path *path, umode_t mode); int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); diff --git a/fs/open.c b/fs/open.c index 91f1139591ab..412d0d6fbaa7 100644 --- a/fs/open.c +++ b/fs/open.c @@ -197,7 +197,7 @@ int do_ftruncate(struct file *file, loff_t length, int small) ATTR_MTIME | ATTR_CTIME, file); } -int do_sys_ftruncate(unsigned int fd, loff_t length, int small) +int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags) { if (length < 0) return -EINVAL; @@ -205,18 +205,18 @@ int do_sys_ftruncate(unsigned int fd, loff_t length, int small) if (fd_empty(f)) return -EBADF; - return do_ftruncate(fd_file(f), length, small); + return do_ftruncate(fd_file(f), length, !(flags & FTRUNCATE_LFS)); } SYSCALL_DEFINE2(ftruncate, unsigned int, fd, off_t, length) { - return do_sys_ftruncate(fd, length, 1); + return ksys_ftruncate(fd, length, 0); } #ifdef CONFIG_COMPAT COMPAT_SYSCALL_DEFINE2(ftruncate, unsigned int, fd, compat_off_t, length) { - return do_sys_ftruncate(fd, length, 1); + return ksys_ftruncate(fd, length, 0); } #endif @@ -229,7 +229,7 @@ SYSCALL_DEFINE2(truncate64, const char __user *, path, loff_t, length) SYSCALL_DEFINE2(ftruncate64, unsigned int, fd, loff_t, length) { - return do_sys_ftruncate(fd, length, 0); + return ksys_ftruncate(fd, length, FTRUNCATE_LFS); } #endif /* BITS_PER_LONG == 32 */ @@ -245,7 +245,7 @@ COMPAT_SYSCALL_DEFINE3(truncate64, const char __user *, pathname, COMPAT_SYSCALL_DEFINE3(ftruncate64, unsigned int, fd, compat_arg_u64_dual(length)) { - return ksys_ftruncate(fd, compat_arg_u64_glue(length)); + return ksys_ftruncate(fd, compat_arg_u64_glue(length), FTRUNCATE_LFS); } #endif diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 02bd6ddb6278..8787b3511c86 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1283,12 +1283,8 @@ static inline long ksys_lchown(const char __user *filename, uid_t user, AT_SYMLINK_NOFOLLOW); } -int do_sys_ftruncate(unsigned int fd, loff_t length, int small); - -static inline long ksys_ftruncate(unsigned int fd, loff_t length) -{ - return do_sys_ftruncate(fd, length, 1); -} +#define FTRUNCATE_LFS (1u << 0) /* allow truncating > 32-bit */ +int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags); int do_sys_truncate(const char __user *pathname, loff_t length); From 0924f6b80d4ac8cc0460fc73de163b562127026d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Mar 2026 08:01:45 +0100 Subject: [PATCH 14/28] fs: pass on FTRUNCATE_* flags to do_truncate Pass the flags one level down to replace the somewhat confusing small argument, and clean up do_truncate as a result. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260323070205.2939118-3-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/internal.h | 2 +- fs/open.c | 22 ++++++++++------------ io_uring/truncate.c | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index 2663823e273a..52e4c354e7a4 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -198,7 +198,7 @@ extern struct open_how build_open_how(int flags, umode_t mode); extern int build_open_flags(const struct open_how *how, struct open_flags *op); struct file *file_close_fd_locked(struct files_struct *files, unsigned fd); -int do_ftruncate(struct file *file, loff_t length, int small); +int do_ftruncate(struct file *file, loff_t length, unsigned int flags); int chmod_common(const struct path *path, umode_t mode); int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); diff --git a/fs/open.c b/fs/open.c index 412d0d6fbaa7..181c1597e73c 100644 --- a/fs/open.c +++ b/fs/open.c @@ -161,23 +161,21 @@ COMPAT_SYSCALL_DEFINE2(truncate, const char __user *, path, compat_off_t, length } #endif -int do_ftruncate(struct file *file, loff_t length, int small) +int do_ftruncate(struct file *file, loff_t length, unsigned int flags) { - struct inode *inode; - struct dentry *dentry; + struct dentry *dentry = file->f_path.dentry; + struct inode *inode = dentry->d_inode; int error; - /* explicitly opened as large or we are on 64-bit box */ - if (file->f_flags & O_LARGEFILE) - small = 0; - - dentry = file->f_path.dentry; - inode = dentry->d_inode; if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE)) return -EINVAL; - /* Cannot ftruncate over 2^31 bytes without large file support */ - if (small && length > MAX_NON_LFS) + /* + * Cannot ftruncate over 2^31 bytes without large file support, either + * through opening with O_LARGEFILE or by using ftruncate64(). + */ + if (length > MAX_NON_LFS && + !(file->f_flags & O_LARGEFILE) && !(flags & FTRUNCATE_LFS)) return -EINVAL; /* Check IS_APPEND on real upper inode */ @@ -205,7 +203,7 @@ int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags) if (fd_empty(f)) return -EBADF; - return do_ftruncate(fd_file(f), length, !(flags & FTRUNCATE_LFS)); + return do_ftruncate(fd_file(f), length, flags); } SYSCALL_DEFINE2(ftruncate, unsigned int, fd, off_t, length) diff --git a/io_uring/truncate.c b/io_uring/truncate.c index 487baf23b44e..c88d8bd8d20e 100644 --- a/io_uring/truncate.c +++ b/io_uring/truncate.c @@ -41,7 +41,7 @@ int io_ftruncate(struct io_kiocb *req, unsigned int issue_flags) WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); - ret = do_ftruncate(req->file, ft->len, 1); + ret = do_ftruncate(req->file, ft->len, 0); io_req_set_res(req, ret, 0); return IOU_COMPLETE; From e8767a3134ca69a307b37f7f58ca088dbee6eb82 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 23 Mar 2026 08:01:46 +0100 Subject: [PATCH 15/28] fs: remove do_sys_truncate do_sys_truncate ist only used to implement ksys_truncate and the native truncate syscalls. Merge do_sys_truncate into ksys_truncate and return int from it as it only returns 0 or negative errnos. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260323070205.2939118-4-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/open.c | 8 ++++---- include/linux/syscalls.h | 8 +------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/fs/open.c b/fs/open.c index 181c1597e73c..681d405bc61e 100644 --- a/fs/open.c +++ b/fs/open.c @@ -126,7 +126,7 @@ int vfs_truncate(const struct path *path, loff_t length) } EXPORT_SYMBOL_GPL(vfs_truncate); -int do_sys_truncate(const char __user *pathname, loff_t length) +int ksys_truncate(const char __user *pathname, loff_t length) { unsigned int lookup_flags = LOOKUP_FOLLOW; struct path path; @@ -151,13 +151,13 @@ int do_sys_truncate(const char __user *pathname, loff_t length) SYSCALL_DEFINE2(truncate, const char __user *, path, long, length) { - return do_sys_truncate(path, length); + return ksys_truncate(path, length); } #ifdef CONFIG_COMPAT COMPAT_SYSCALL_DEFINE2(truncate, const char __user *, path, compat_off_t, length) { - return do_sys_truncate(path, length); + return ksys_truncate(path, length); } #endif @@ -222,7 +222,7 @@ COMPAT_SYSCALL_DEFINE2(ftruncate, unsigned int, fd, compat_off_t, length) #if BITS_PER_LONG == 32 SYSCALL_DEFINE2(truncate64, const char __user *, path, loff_t, length) { - return do_sys_truncate(path, length); + return ksys_truncate(path, length); } SYSCALL_DEFINE2(ftruncate64, unsigned int, fd, loff_t, length) diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 8787b3511c86..f5639d5ac331 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1285,13 +1285,7 @@ static inline long ksys_lchown(const char __user *filename, uid_t user, #define FTRUNCATE_LFS (1u << 0) /* allow truncating > 32-bit */ int ksys_ftruncate(unsigned int fd, loff_t length, unsigned int flags); - -int do_sys_truncate(const char __user *pathname, loff_t length); - -static inline long ksys_truncate(const char __user *pathname, loff_t length) -{ - return do_sys_truncate(pathname, length); -} +int ksys_truncate(const char __user *pathname, loff_t length); static inline unsigned int ksys_personality(unsigned int personality) { From f30186b0c7829841744a40f7345e6cc9865f8a67 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Mon, 23 Mar 2026 04:46:27 -0700 Subject: [PATCH 16/28] coredump: add tracepoint for coredump events Coredump is a generally useful and interesting event in the lifetime of a process. Add a tracepoint so it can be monitored through the standard kernel tracing infrastructure. BPF-based crash monitoring is an advanced approach that allows real-time crash interception: by attaching a BPF program at this point, tools can use bpf_get_stack() with BPF_F_USER_STACK to capture the user-space stack trace at the exact moment of the crash, before the process is fully terminated, without waiting for a coredump file to be written and parsed. However, there is currently no stable kernel API for this use case. Existing tools rely on attaching fentry probes to do_coredump(), which is an internal function whose signature changes across kernel versions, breaking these tools. Add a stable tracepoint that fires at the beginning of do_coredump(), providing BPF programs a reliable attachment point. At tracepoint time, the crashing process context is still live, so BPF programs can call bpf_get_stack() with BPF_F_USER_STACK to extract the user-space backtrace. The tracepoint records: - sig: signal number that triggered the coredump - comm: process name Example output: $ echo 1 > /sys/kernel/tracing/events/coredump/coredump/enable $ sleep 999 & $ kill -SEGV $! $ cat /sys/kernel/tracing/trace # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | sleep-634 [036] ..... 145.222206: coredump: sig=11 comm=sleep Suggested-by: Andrii Nakryiko Signed-off-by: Breno Leitao Link: https://patch.msgid.link/20260323-coredump_tracepoint-v2-1-afced083b38d@debian.org Signed-off-by: Christian Brauner --- fs/coredump.c | 5 ++++ include/trace/events/coredump.h | 45 +++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 include/trace/events/coredump.h diff --git a/fs/coredump.c b/fs/coredump.c index 4ce7c80b39c8..bc7009b3eedc 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -63,6 +63,9 @@ #include +#define CREATE_TRACE_POINTS +#include + static bool dump_vma_snapshot(struct coredump_params *cprm); static void free_vma_snapshot(struct coredump_params *cprm); @@ -1090,6 +1093,8 @@ static inline bool coredump_skip(const struct coredump_params *cprm, static void do_coredump(struct core_name *cn, struct coredump_params *cprm, size_t **argv, int *argc, const struct linux_binfmt *binfmt) { + trace_coredump(cprm->siginfo->si_signo); + if (!coredump_parse(cn, cprm, argv, argc)) { coredump_report_failure("format_corename failed, aborting core"); return; diff --git a/include/trace/events/coredump.h b/include/trace/events/coredump.h new file mode 100644 index 000000000000..c7b9c53fc498 --- /dev/null +++ b/include/trace/events/coredump.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + * Copyright (c) 2026 Breno Leitao + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM coredump + +#if !defined(_TRACE_COREDUMP_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_COREDUMP_H + +#include +#include + +/** + * coredump - called when a coredump starts + * @sig: signal number that triggered the coredump + * + * This tracepoint fires at the beginning of a coredump attempt, + * providing a stable interface for monitoring coredump events. + */ +TRACE_EVENT(coredump, + + TP_PROTO(int sig), + + TP_ARGS(sig), + + TP_STRUCT__entry( + __field(int, sig) + __array(char, comm, TASK_COMM_LEN) + ), + + TP_fast_assign( + __entry->sig = sig; + memcpy(__entry->comm, current->comm, TASK_COMM_LEN); + ), + + TP_printk("sig=%d comm=%s", + __entry->sig, __entry->comm) +); + +#endif /* _TRACE_COREDUMP_H */ + +/* This part must be outside protection */ +#include From 4bf798e027d35e4fd9a31b32e6bc2d33a73c0041 Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Tue, 24 Mar 2026 12:41:15 +0100 Subject: [PATCH 17/28] readdir: Introduce dirent_size() In several places in readdir.c there are calculations of the total size of a dirent, which contains a few fixed fields plus a name field with variable size. To add fun every dirent is of a slightly different type: - struct old_linux_dirent - struct linux_dirent - struct linux_dirent64 - struct compat_old_linux_dirent - struct compat_linux_dirent Replace ugly size calculation by a macro called dirent_size() which calculates the size of the structure based on the pointed type and the name field len. Suggested-by: Linus Torvalds Signed-off-by: Christophe Leroy (CS GROUP) Link: https://patch.msgid.link/c20d2f8f6817a39401155cfc80f0dff88df116e0.1774350128.git.chleroy@kernel.org Reviewed-by: David Laight Signed-off-by: Christian Brauner --- fs/readdir.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/fs/readdir.c b/fs/readdir.c index 73707b6816e9..fb910dc2f52b 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -22,6 +22,8 @@ #include #include +#define dirent_size(dirent, len) offsetof(typeof(*(dirent)), d_name[len]) + /* * Some filesystems were never converted to '->iterate_shared()' * and their directory iterators want the inode lock held for @@ -198,9 +200,7 @@ static bool fillonedir(struct dir_context *ctx, const char *name, int namlen, } buf->result++; dirent = buf->dirent; - if (!user_write_access_begin(dirent, - (unsigned long)(dirent->d_name + namlen + 1) - - (unsigned long)dirent)) + if (!user_write_access_begin(dirent, dirent_size(dirent, namlen + 1))) goto efault; unsafe_put_user(d_ino, &dirent->d_ino, efault_end); unsafe_put_user(offset, &dirent->d_offset, efault_end); @@ -263,8 +263,7 @@ static bool filldir(struct dir_context *ctx, const char *name, int namlen, struct getdents_callback *buf = container_of(ctx, struct getdents_callback, ctx); unsigned long d_ino; - int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, - sizeof(long)); + int reclen = ALIGN(dirent_size(dirent, namlen + 2), sizeof(long)); int prev_reclen; unsigned int flags = d_type; @@ -352,8 +351,7 @@ static bool filldir64(struct dir_context *ctx, const char *name, int namlen, struct linux_dirent64 __user *dirent, *prev; struct getdents_callback64 *buf = container_of(ctx, struct getdents_callback64, ctx); - int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, - sizeof(u64)); + int reclen = ALIGN(dirent_size(dirent, namlen + 1), sizeof(u64)); int prev_reclen; unsigned int flags = d_type; @@ -460,9 +458,7 @@ static bool compat_fillonedir(struct dir_context *ctx, const char *name, } buf->result++; dirent = buf->dirent; - if (!user_write_access_begin(dirent, - (unsigned long)(dirent->d_name + namlen + 1) - - (unsigned long)dirent)) + if (!user_write_access_begin(dirent, dirent_size(dirent, namlen + 1))) goto efault; unsafe_put_user(d_ino, &dirent->d_ino, efault_end); unsafe_put_user(offset, &dirent->d_offset, efault_end); @@ -519,8 +515,7 @@ static bool compat_filldir(struct dir_context *ctx, const char *name, int namlen struct compat_getdents_callback *buf = container_of(ctx, struct compat_getdents_callback, ctx); compat_ulong_t d_ino; - int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) + - namlen + 2, sizeof(compat_long_t)); + int reclen = ALIGN(dirent_size(dirent, namlen + 2), sizeof(compat_long_t)); int prev_reclen; unsigned int flags = d_type; From b98f7363f72ff83b9f5194d26e7f9fe74f45b46a Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Tue, 24 Mar 2026 12:41:16 +0100 Subject: [PATCH 18/28] fs: Replace user_access_{begin/end} by scoped user access Scoped user access reduces code complexity and seamlessly bring masked user access on architectures that support it. Replace user_access_begin/user_access_end blocks by scoped user access. Signed-off-by: Christophe Leroy (CS GROUP) Link: https://patch.msgid.link/16daf33a8190a771a93e294d050bd8153521ffca.1774350128.git.chleroy@kernel.org Signed-off-by: Christian Brauner --- fs/readdir.c | 88 +++++++++++++++++++++------------------------------- fs/select.c | 35 +++++++++------------ 2 files changed, 49 insertions(+), 74 deletions(-) diff --git a/fs/readdir.c b/fs/readdir.c index fb910dc2f52b..76bb1ae3a450 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -200,16 +200,13 @@ static bool fillonedir(struct dir_context *ctx, const char *name, int namlen, } buf->result++; dirent = buf->dirent; - if (!user_write_access_begin(dirent, dirent_size(dirent, namlen + 1))) - goto efault; - unsafe_put_user(d_ino, &dirent->d_ino, efault_end); - unsafe_put_user(offset, &dirent->d_offset, efault_end); - unsafe_put_user(namlen, &dirent->d_namlen, efault_end); - unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); - user_write_access_end(); + scoped_user_write_access_size(dirent, dirent_size(dirent, namlen + 1), efault) { + unsafe_put_user(d_ino, &dirent->d_ino, efault); + unsafe_put_user(offset, &dirent->d_offset, efault); + unsafe_put_user(namlen, &dirent->d_namlen, efault); + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault); + } return true; -efault_end: - user_write_access_end(); efault: buf->result = -EFAULT; return false; @@ -286,23 +283,19 @@ static bool filldir(struct dir_context *ctx, const char *name, int namlen, return false; dirent = buf->current_dir; prev = (void __user *) dirent - prev_reclen; - if (!user_write_access_begin(prev, reclen + prev_reclen)) - goto efault; - - /* This might be 'dirent->d_off', but if so it will get overwritten */ - unsafe_put_user(offset, &prev->d_off, efault_end); - unsafe_put_user(d_ino, &dirent->d_ino, efault_end); - unsafe_put_user(reclen, &dirent->d_reclen, efault_end); - unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end); - unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); - user_write_access_end(); + scoped_user_write_access_size(prev, reclen + prev_reclen, efault) { + /* This might be 'dirent->d_off', but if so it will get overwritten */ + unsafe_put_user(offset, &prev->d_off, efault); + unsafe_put_user(d_ino, &dirent->d_ino, efault); + unsafe_put_user(reclen, &dirent->d_reclen, efault); + unsafe_put_user(d_type, (char __user *)dirent + reclen - 1, efault); + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault); + } buf->current_dir = (void __user *)dirent + reclen; buf->prev_reclen = reclen; ctx->count -= reclen; return true; -efault_end: - user_write_access_end(); efault: buf->error = -EFAULT; return false; @@ -369,24 +362,20 @@ static bool filldir64(struct dir_context *ctx, const char *name, int namlen, return false; dirent = buf->current_dir; prev = (void __user *)dirent - prev_reclen; - if (!user_write_access_begin(prev, reclen + prev_reclen)) - goto efault; - - /* This might be 'dirent->d_off', but if so it will get overwritten */ - unsafe_put_user(offset, &prev->d_off, efault_end); - unsafe_put_user(ino, &dirent->d_ino, efault_end); - unsafe_put_user(reclen, &dirent->d_reclen, efault_end); - unsafe_put_user(d_type, &dirent->d_type, efault_end); - unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); - user_write_access_end(); + scoped_user_write_access_size(prev, reclen + prev_reclen, efault) { + /* This might be 'dirent->d_off', but if so it will get overwritten */ + unsafe_put_user(offset, &prev->d_off, efault); + unsafe_put_user(ino, &dirent->d_ino, efault); + unsafe_put_user(reclen, &dirent->d_reclen, efault); + unsafe_put_user(d_type, &dirent->d_type, efault); + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault); + } buf->prev_reclen = reclen; buf->current_dir = (void __user *)dirent + reclen; ctx->count -= reclen; return true; -efault_end: - user_write_access_end(); efault: buf->error = -EFAULT; return false; @@ -458,16 +447,13 @@ static bool compat_fillonedir(struct dir_context *ctx, const char *name, } buf->result++; dirent = buf->dirent; - if (!user_write_access_begin(dirent, dirent_size(dirent, namlen + 1))) - goto efault; - unsafe_put_user(d_ino, &dirent->d_ino, efault_end); - unsafe_put_user(offset, &dirent->d_offset, efault_end); - unsafe_put_user(namlen, &dirent->d_namlen, efault_end); - unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); - user_write_access_end(); + scoped_user_write_access_size(dirent, dirent_size(dirent, namlen + 1), efault) { + unsafe_put_user(d_ino, &dirent->d_ino, efault); + unsafe_put_user(offset, &dirent->d_offset, efault); + unsafe_put_user(namlen, &dirent->d_namlen, efault); + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault); + } return true; -efault_end: - user_write_access_end(); efault: buf->result = -EFAULT; return false; @@ -538,22 +524,18 @@ static bool compat_filldir(struct dir_context *ctx, const char *name, int namlen return false; dirent = buf->current_dir; prev = (void __user *) dirent - prev_reclen; - if (!user_write_access_begin(prev, reclen + prev_reclen)) - goto efault; - - unsafe_put_user(offset, &prev->d_off, efault_end); - unsafe_put_user(d_ino, &dirent->d_ino, efault_end); - unsafe_put_user(reclen, &dirent->d_reclen, efault_end); - unsafe_put_user(d_type, (char __user *) dirent + reclen - 1, efault_end); - unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault_end); - user_write_access_end(); + scoped_user_write_access_size(prev, reclen + prev_reclen, efault) { + unsafe_put_user(offset, &prev->d_off, efault); + unsafe_put_user(d_ino, &dirent->d_ino, efault); + unsafe_put_user(reclen, &dirent->d_reclen, efault); + unsafe_put_user(d_type, (char __user *)dirent + reclen - 1, efault); + unsafe_copy_dirent_name(dirent->d_name, name, namlen, efault); + } buf->prev_reclen = reclen; buf->current_dir = (void __user *)dirent + reclen; ctx->count -= reclen; return true; -efault_end: - user_write_access_end(); efault: buf->error = -EFAULT; return false; diff --git a/fs/select.c b/fs/select.c index 78a1508c84d3..eef27707f912 100644 --- a/fs/select.c +++ b/fs/select.c @@ -1005,17 +1005,17 @@ static int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, fdcount = do_poll(head, &table, end_time); poll_freewait(&table); - if (!user_write_access_begin(ufds, nfds * sizeof(*ufds))) - goto out_fds; + scoped_user_write_access_size(ufds, nfds * sizeof(*ufds), out_fds) { + struct pollfd __user *_ufds = ufds; - for (walk = head; walk; walk = walk->next) { - struct pollfd *fds = walk->entries; - unsigned int j; + for (walk = head; walk; walk = walk->next) { + struct pollfd *fds = walk->entries; + unsigned int j; - for (j = walk->len; j; fds++, ufds++, j--) - unsafe_put_user(fds->revents, &ufds->revents, Efault); - } - user_write_access_end(); + for (j = walk->len; j; fds++, _ufds++, j--) + unsafe_put_user(fds->revents, &_ufds->revents, out_fds); + } + } err = fdcount; out_fds: @@ -1027,11 +1027,6 @@ static int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, } return err; - -Efault: - user_write_access_end(); - err = -EFAULT; - goto out_fds; } static long do_restart_poll(struct restart_block *restart_block) @@ -1339,15 +1334,13 @@ static inline int get_compat_sigset_argpack(struct compat_sigset_argpack *to, struct compat_sigset_argpack __user *from) { if (from) { - if (!user_read_access_begin(from, sizeof(*from))) - return -EFAULT; - unsafe_get_user(to->p, &from->p, Efault); - unsafe_get_user(to->size, &from->size, Efault); - user_read_access_end(); + scoped_user_read_access(from, efault) { + unsafe_get_user(to->p, &from->p, efault); + unsafe_get_user(to->size, &from->size, efault); + } } return 0; -Efault: - user_read_access_end(); +efault: return -EFAULT; } From 388727ef1cdb5b92c482f53d6636557372c2ea1a Mon Sep 17 00:00:00 2001 From: "haoyu.lu" Date: Thu, 26 Mar 2026 13:56:27 +0800 Subject: [PATCH 19/28] kernel: acct: fix duplicate word in comment Fix the duplicate word "kernel" in the comment on line 247. Signed-off-by: haoyu.lu Link: https://patch.msgid.link/20260326055628.10773-1-hechushiguitu666@gmail.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- kernel/acct.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/acct.c b/kernel/acct.c index 812808e5b1b8..43f70a082bf9 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -244,7 +244,7 @@ static int acct_on(const char __user *name) if (!S_ISREG(file_inode(file)->i_mode)) return -EACCES; - /* Exclude kernel kernel internal filesystems. */ + /* Exclude kernel internal filesystems. */ if (file_inode(file)->i_sb->s_flags & (SB_NOUSER | SB_KERNMOUNT)) return -EINVAL; From d1312979f155597cad51ad61f845e59114bba0f8 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 25 Mar 2026 07:36:48 +0100 Subject: [PATCH 20/28] hfs: update comments on hfs_inode_setattr The top of function comment about hfs_inode_setattr is severely out of date and reference a previous name for this function. Remove it, and update the comments in the file to record the still relevant bits directly. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260325063711.3298685-2-hch@lst.de Reviewed-by: Viacheslav Dubeyko Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/hfs/inode.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 878535db64d6..72948eb2fadc 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -622,23 +622,6 @@ static int hfs_file_release(struct inode *inode, struct file *file) return 0; } -/* - * hfs_notify_change() - * - * Based very closely on fs/msdos/inode.c by Werner Almesberger - * - * This is the notify_change() field in the super_operations structure - * for HFS file systems. The purpose is to take that changes made to - * an inode and apply then in a filesystem-dependent manner. In this - * case the process has a few of tasks to do: - * 1) prevent changes to the i_uid and i_gid fields. - * 2) map file permissions to the closest allowable permissions - * 3) Since multiple Linux files can share the same on-disk inode under - * HFS (for instance the data and resource forks of a file) a change - * to permissions must be applied to all other in-core inodes which - * correspond to the same HFS file. - */ - int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { @@ -646,8 +629,7 @@ int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct hfs_sb_info *hsb = HFS_SB(inode->i_sb); int error; - error = setattr_prepare(&nop_mnt_idmap, dentry, - attr); /* basic permission checks */ + error = setattr_prepare(&nop_mnt_idmap, dentry, attr); if (error) return error; @@ -663,6 +645,7 @@ int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, return hsb->s_quiet ? 0 : error; } + /* map file permissions to the closest allowable permissions in HFS */ if (attr->ia_valid & ATTR_MODE) { /* Only the 'w' bits can ever change and only all together. */ if (attr->ia_mode & S_IWUSR) From 9c71de5f4ddc978782f7ceb60c8cfee4db64f286 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 25 Mar 2026 07:36:49 +0100 Subject: [PATCH 21/28] adfs: rename adfs_notify_change to adfs_setattr Make the function name match the method that it implements. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260325063711.3298685-3-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/adfs/adfs.h | 4 ++-- fs/adfs/dir.c | 2 +- fs/adfs/file.c | 2 +- fs/adfs/inode.c | 6 ++---- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index 223f0283d20f..0d32b7cd99b4 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -144,8 +144,8 @@ struct adfs_discmap { /* Inode stuff */ struct inode *adfs_iget(struct super_block *sb, struct object_info *obj); int adfs_write_inode(struct inode *inode, struct writeback_control *wbc); -int adfs_notify_change(struct mnt_idmap *idmap, struct dentry *dentry, - struct iattr *attr); +int adfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr); /* map.c */ int adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset); diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 4f9dc276da6f..e900ec9d0066 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -454,5 +454,5 @@ adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) */ const struct inode_operations adfs_dir_inode_operations = { .lookup = adfs_lookup, - .setattr = adfs_notify_change, + .setattr = adfs_setattr, }; diff --git a/fs/adfs/file.c b/fs/adfs/file.c index cd13165fd904..73486667d6b4 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -32,5 +32,5 @@ const struct file_operations adfs_file_operations = { }; const struct inode_operations adfs_file_inode_operations = { - .setattr = adfs_notify_change, + .setattr = adfs_setattr, }; diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 6830f8bc8d4e..4ac442d0a8c0 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -299,8 +299,7 @@ adfs_iget(struct super_block *sb, struct object_info *obj) * later. */ int -adfs_notify_change(struct mnt_idmap *idmap, struct dentry *dentry, - struct iattr *attr) +adfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { struct inode *inode = d_inode(dentry); struct super_block *sb = inode->i_sb; @@ -355,8 +354,7 @@ adfs_notify_change(struct mnt_idmap *idmap, struct dentry *dentry, /* * write an existing inode back to the directory, and therefore the disk. - * The adfs-specific inode data has already been updated by - * adfs_notify_change() + * The adfs-specific inode data has already been updated by * adfs_setattr(). */ int adfs_write_inode(struct inode *inode, struct writeback_control *wbc) { From d0fdc1c0d0f6bb844fdf1c135d6b0f310e58d794 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 25 Mar 2026 07:36:50 +0100 Subject: [PATCH 22/28] affs: rename affs_notify_change to affs_setattr Make the function name match the method that it implements. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260325063711.3298685-4-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/affs/affs.h | 2 +- fs/affs/dir.c | 2 +- fs/affs/file.c | 2 +- fs/affs/inode.c | 5 ++--- fs/affs/symlink.c | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/fs/affs/affs.h b/fs/affs/affs.h index ac4e9a02910b..43e20b02b449 100644 --- a/fs/affs/affs.h +++ b/fs/affs/affs.h @@ -184,7 +184,7 @@ extern int affs_rename2(struct mnt_idmap *idmap, /* inode.c */ extern struct inode *affs_new_inode(struct inode *dir); -extern int affs_notify_change(struct mnt_idmap *idmap, +extern int affs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr); extern void affs_evict_inode(struct inode *inode); extern struct inode *affs_iget(struct super_block *sb, diff --git a/fs/affs/dir.c b/fs/affs/dir.c index fe18caaf4d65..5f3d5d54085e 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -71,7 +71,7 @@ const struct inode_operations affs_dir_inode_operations = { .mkdir = affs_mkdir, .rmdir = affs_rmdir, .rename = affs_rename2, - .setattr = affs_notify_change, + .setattr = affs_setattr, }; static int diff --git a/fs/affs/file.c b/fs/affs/file.c index 6c9258359ddb..cede4bb1134e 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -1013,5 +1013,5 @@ const struct file_operations affs_file_operations = { }; const struct inode_operations affs_file_inode_operations = { - .setattr = affs_notify_change, + .setattr = affs_setattr, }; diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 0bfc7d151dcd..f660e76584b3 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -213,13 +213,12 @@ affs_write_inode(struct inode *inode, struct writeback_control *wbc) } int -affs_notify_change(struct mnt_idmap *idmap, struct dentry *dentry, - struct iattr *attr) +affs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { struct inode *inode = d_inode(dentry); int error; - pr_debug("notify_change(%lu,0x%x)\n", inode->i_ino, attr->ia_valid); + pr_debug("setattr(%lu,0x%x)\n", inode->i_ino, attr->ia_valid); error = setattr_prepare(&nop_mnt_idmap, dentry, attr); if (error) diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c index 094aec8d17b8..af6147e1d975 100644 --- a/fs/affs/symlink.c +++ b/fs/affs/symlink.c @@ -71,5 +71,5 @@ const struct address_space_operations affs_symlink_aops = { const struct inode_operations affs_symlink_inode_operations = { .get_link = page_get_link, - .setattr = affs_notify_change, + .setattr = affs_setattr, }; From 690005b0b1e6b567c88b7790e6d90d4d6c9e09cc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 25 Mar 2026 07:36:51 +0100 Subject: [PATCH 23/28] proc: rename proc_setattr to proc_nochmod_setattr What is currently proc_setattr is a special version added after the more general procfs ->seattr in commit 6d76fa58b050 ("Don't allow chmod() on the /proc// files"). Give it a name that reflects that to free the proc_setattr name and better describe what is doing. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260325063711.3298685-5-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/proc/base.c | 22 +++++++++++----------- fs/proc/fd.c | 6 +++--- fs/proc/internal.h | 4 ++-- fs/proc/namespaces.c | 4 ++-- fs/proc/proc_net.c | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 4eec684baca9..76660614875c 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -721,7 +721,7 @@ static bool proc_fd_access_allowed(struct inode *inode) return allowed; } -int proc_setattr(struct mnt_idmap *idmap, struct dentry *dentry, +int proc_nochmod_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr) { int error; @@ -794,7 +794,7 @@ static int proc_pid_permission(struct mnt_idmap *idmap, static const struct inode_operations proc_def_inode_operations = { - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static int proc_single_show(struct seq_file *m, void *v) @@ -1866,7 +1866,7 @@ static int proc_pid_readlink(struct dentry * dentry, char __user * buffer, int b const struct inode_operations proc_pid_link_inode_operations = { .readlink = proc_pid_readlink, .get_link = proc_pid_get_link, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; @@ -2316,7 +2316,7 @@ proc_map_files_get_link(struct dentry *dentry, static const struct inode_operations proc_map_files_link_inode_operations = { .readlink = proc_pid_readlink, .get_link = proc_map_files_get_link, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry * @@ -2395,7 +2395,7 @@ static struct dentry *proc_map_files_lookup(struct inode *dir, static const struct inode_operations proc_map_files_inode_operations = { .lookup = proc_map_files_lookup, .permission = proc_fd_permission, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static int @@ -2882,7 +2882,7 @@ static struct dentry *proc_##LSM##_attr_dir_lookup(struct inode *dir, \ static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \ .lookup = proc_##LSM##_attr_dir_lookup, \ .getattr = pid_getattr, \ - .setattr = proc_setattr, \ + .setattr = proc_nochmod_setattr, \ } #ifdef CONFIG_SECURITY_SMACK @@ -2941,7 +2941,7 @@ static struct dentry *proc_attr_dir_lookup(struct inode *dir, static const struct inode_operations proc_attr_dir_inode_operations = { .lookup = proc_attr_dir_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; #endif @@ -3450,7 +3450,7 @@ static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *de static const struct inode_operations proc_tgid_base_inode_operations = { .lookup = proc_tgid_base_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, .permission = proc_pid_permission, }; @@ -3647,7 +3647,7 @@ static int proc_tid_comm_permission(struct mnt_idmap *idmap, } static const struct inode_operations proc_tid_comm_inode_operations = { - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, .permission = proc_tid_comm_permission, }; @@ -3776,7 +3776,7 @@ static const struct file_operations proc_tid_base_operations = { static const struct inode_operations proc_tid_base_inode_operations = { .lookup = proc_tid_base_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry *proc_task_instantiate(struct dentry *dentry, @@ -3989,7 +3989,7 @@ static loff_t proc_dir_llseek(struct file *file, loff_t offset, int whence) static const struct inode_operations proc_task_inode_operations = { .lookup = proc_task_lookup, .getattr = proc_task_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, .permission = proc_pid_permission, }; diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 9eeccff49b2a..8a48ff4a19ec 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -102,7 +102,7 @@ static int proc_fdinfo_permission(struct mnt_idmap *idmap, struct inode *inode, static const struct inode_operations proc_fdinfo_file_inode_operations = { .permission = proc_fdinfo_permission, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static const struct file_operations proc_fdinfo_file_operations = { @@ -361,7 +361,7 @@ const struct inode_operations proc_fd_inode_operations = { .lookup = proc_lookupfd, .permission = proc_fd_permission, .getattr = proc_fd_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry *proc_fdinfo_instantiate(struct dentry *dentry, @@ -402,7 +402,7 @@ static int proc_fdinfo_iterate(struct file *file, struct dir_context *ctx) const struct inode_operations proc_fdinfo_inode_operations = { .lookup = proc_lookupfdinfo, .permission = proc_fdinfo_permission, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; const struct file_operations proc_fdinfo_operations = { diff --git a/fs/proc/internal.h b/fs/proc/internal.h index c1e8eb984da8..64dc44832808 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -257,8 +257,8 @@ extern int proc_pid_statm(struct seq_file *, struct pid_namespace *, extern const struct dentry_operations pid_dentry_operations; extern int pid_getattr(struct mnt_idmap *, const struct path *, struct kstat *, u32, unsigned int); -extern int proc_setattr(struct mnt_idmap *, struct dentry *, - struct iattr *); +int proc_nochmod_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr); extern void proc_pid_evict_inode(struct proc_inode *); extern struct inode *proc_pid_make_inode(struct super_block *, struct task_struct *, umode_t); extern void pid_update_inode(struct task_struct *, struct inode *); diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index ea2b597fd92c..39f4169f669f 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -92,7 +92,7 @@ static int proc_ns_readlink(struct dentry *dentry, char __user *buffer, int bufl static const struct inode_operations proc_ns_link_inode_operations = { .readlink = proc_ns_readlink, .get_link = proc_ns_get_link, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static struct dentry *proc_ns_instantiate(struct dentry *dentry, @@ -178,5 +178,5 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir, const struct inode_operations proc_ns_dir_inode_operations = { .lookup = proc_ns_dir_lookup, .getattr = pid_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 52f0b75cbce2..184cddeb8215 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -322,7 +322,7 @@ static int proc_tgid_net_getattr(struct mnt_idmap *idmap, const struct inode_operations proc_net_inode_operations = { .lookup = proc_tgid_net_lookup, .getattr = proc_tgid_net_getattr, - .setattr = proc_setattr, + .setattr = proc_nochmod_setattr, }; static int proc_tgid_net_readdir(struct file *file, struct dir_context *ctx) From 2ecd46d161faf4b038e02950c9d621a1ffcbd321 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 25 Mar 2026 07:36:52 +0100 Subject: [PATCH 24/28] proc: rename proc_notify_change to proc_setattr Make the function name match the method that it implements. Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260325063711.3298685-6-hch@lst.de Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/proc/generic.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 501889856461..8bb81e58c9d8 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -115,8 +115,8 @@ static bool pde_subdir_insert(struct proc_dir_entry *dir, return true; } -static int proc_notify_change(struct mnt_idmap *idmap, - struct dentry *dentry, struct iattr *iattr) +static int proc_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *iattr) { struct inode *inode = d_inode(dentry); struct proc_dir_entry *de = PDE(inode); @@ -151,7 +151,7 @@ static int proc_getattr(struct mnt_idmap *idmap, } static const struct inode_operations proc_file_inode_operations = { - .setattr = proc_notify_change, + .setattr = proc_setattr, }; /* @@ -364,7 +364,7 @@ const struct dentry_operations proc_net_dentry_ops = { static const struct inode_operations proc_dir_inode_operations = { .lookup = proc_lookup, .getattr = proc_getattr, - .setattr = proc_notify_change, + .setattr = proc_setattr, }; static void pde_set_flags(struct proc_dir_entry *pde) From 18f2e0ea200cdacf59a1c19a3ac4790d2ebe59af Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Sat, 28 Mar 2026 18:58:40 +0100 Subject: [PATCH 25/28] fs: write a better comment in step_into() concerning .mnt assignment Signed-off-by: Mateusz Guzik Link: https://patch.msgid.link/20260328175841.3390950-1-mjguzik@gmail.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index 5fe6cac48df8..d77f14f8d67c 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2141,7 +2141,7 @@ static __always_inline const char *step_into(struct nameidata *nd, int flags, if (unlikely(!inode)) return ERR_PTR(-ENOENT); nd->path.dentry = dentry; - /* nd->path.mnt is retained on purpose */ + /* nd->path.mnt remains unchanged as no mount point was crossed */ nd->inode = inode; nd->seq = nd->next_seq; return NULL; From 1f1651d6dc2ac282d07043358824273c15a1cac4 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Sat, 28 Mar 2026 18:37:28 +0100 Subject: [PATCH 26/28] fs: hide file and bfile caches behind runtime const machinery s/cachep/cache/ for consistency with namei and dentry caches. Signed-off-by: Mateusz Guzik Link: https://patch.msgid.link/20260328173728.3388070-1-mjguzik@gmail.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/file.c | 2 +- fs/file_table.c | 31 +++++++++++++++++++------------ include/asm-generic/vmlinux.lds.h | 4 +++- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/fs/file.c b/fs/file.c index 51ddcff0081a..290ebcaea927 100644 --- a/fs/file.c +++ b/fs/file.c @@ -200,7 +200,7 @@ static struct fdtable *alloc_fdtable(unsigned int slots_wanted) /* * Check if the allocation size would exceed INT_MAX. kvmalloc_array() * and kvmalloc() will warn if the allocation size is greater than - * INT_MAX, as filp_cachep objects are not __GFP_NOWARN. + * INT_MAX, as filp_cache objects are not __GFP_NOWARN. * * This can happen when sysctl_nr_open is set to a very high value and * a process tries to use a file descriptor near that limit. For example, diff --git a/fs/file_table.c b/fs/file_table.c index aaa5faaace1e..c40ec1be2899 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -30,6 +30,8 @@ #include +#include + #include "internal.h" /* sysctl tunables... */ @@ -38,8 +40,10 @@ static struct files_stat_struct files_stat = { }; /* SLAB cache for file structures */ -static struct kmem_cache *filp_cachep __ro_after_init; -static struct kmem_cache *bfilp_cachep __ro_after_init; +static struct kmem_cache *__filp_cache __ro_after_init; +#define filp_cache runtime_const_ptr(__filp_cache) +static struct kmem_cache *__bfilp_cache __ro_after_init; +#define bfilp_cache runtime_const_ptr(__bfilp_cache) static struct percpu_counter nr_files __cacheline_aligned_in_smp; @@ -74,9 +78,9 @@ static inline void file_free(struct file *f) put_cred(f->f_cred); if (unlikely(f->f_mode & FMODE_BACKING)) { path_put(backing_file_user_path(f)); - kmem_cache_free(bfilp_cachep, backing_file(f)); + kmem_cache_free(bfilp_cache, backing_file(f)); } else { - kmem_cache_free(filp_cachep, f); + kmem_cache_free(filp_cache, f); } } @@ -234,13 +238,13 @@ struct file *alloc_empty_file(int flags, const struct cred *cred) goto over; } - f = kmem_cache_alloc(filp_cachep, GFP_KERNEL); + f = kmem_cache_alloc(filp_cache, GFP_KERNEL); if (unlikely(!f)) return ERR_PTR(-ENOMEM); error = init_file(f, flags, cred); if (unlikely(error)) { - kmem_cache_free(filp_cachep, f); + kmem_cache_free(filp_cache, f); return ERR_PTR(error); } @@ -268,13 +272,13 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) struct file *f; int error; - f = kmem_cache_alloc(filp_cachep, GFP_KERNEL); + f = kmem_cache_alloc(filp_cache, GFP_KERNEL); if (unlikely(!f)) return ERR_PTR(-ENOMEM); error = init_file(f, flags, cred); if (unlikely(error)) { - kmem_cache_free(filp_cachep, f); + kmem_cache_free(filp_cache, f); return ERR_PTR(error); } @@ -295,13 +299,13 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred) struct backing_file *ff; int error; - ff = kmem_cache_alloc(bfilp_cachep, GFP_KERNEL); + ff = kmem_cache_alloc(bfilp_cache, GFP_KERNEL); if (unlikely(!ff)) return ERR_PTR(-ENOMEM); error = init_file(&ff->file, flags, cred); if (unlikely(error)) { - kmem_cache_free(bfilp_cachep, ff); + kmem_cache_free(bfilp_cache, ff); return ERR_PTR(error); } @@ -593,14 +597,17 @@ void __init files_init(void) .freeptr_offset = offsetof(struct file, f_freeptr), }; - filp_cachep = kmem_cache_create("filp", sizeof(struct file), &args, + __filp_cache = kmem_cache_create("filp", sizeof(struct file), &args, SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT | SLAB_TYPESAFE_BY_RCU); + runtime_const_init(ptr, __filp_cache); args.freeptr_offset = offsetof(struct backing_file, bf_freeptr); - bfilp_cachep = kmem_cache_create("bfilp", sizeof(struct backing_file), + __bfilp_cache = kmem_cache_create("bfilp", sizeof(struct backing_file), &args, SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT | SLAB_TYPESAFE_BY_RCU); + runtime_const_init(ptr, __bfilp_cache); + percpu_counter_init(&nr_files, 0, GFP_KERNEL); } diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index eeb070f330bd..6a86f2e004bc 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -973,7 +973,9 @@ RUNTIME_CONST(shift, d_hash_shift) \ RUNTIME_CONST(ptr, dentry_hashtable) \ RUNTIME_CONST(ptr, __dentry_cache) \ - RUNTIME_CONST(ptr, __names_cache) + RUNTIME_CONST(ptr, __names_cache) \ + RUNTIME_CONST(ptr, __filp_cache) \ + RUNTIME_CONST(ptr, __bfilp_cache) /* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */ #define KUNIT_TABLE() \ From 4639f1cfba03e77e4fa2062cf904de63021fb746 Mon Sep 17 00:00:00 2001 From: Chelsy Ratnawat Date: Fri, 3 Apr 2026 02:27:09 -0700 Subject: [PATCH 27/28] fs: attr: fix comment formatting and spelling issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix minor comment issues in fs/attr.c reported by checkpatch: - Wrap long comment lines to comply with the 75-character limit - Correct spelling of “overriden” to “overridden” No functional changes. Signed-off-by: Chelsy Ratnawat Link: https://patch.msgid.link/20260403092709.83458-1-chelsyratnawat2001@gmail.com Signed-off-by: Christian Brauner --- fs/attr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/attr.c b/fs/attr.c index e7d7c6d19fe9..ded221defae6 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -46,8 +46,8 @@ int setattr_should_drop_sgid(struct mnt_idmap *idmap, EXPORT_SYMBOL(setattr_should_drop_sgid); /** - * setattr_should_drop_suidgid - determine whether the set{g,u}id bit needs to - * be dropped + * setattr_should_drop_suidgid - determine whether the set{g,u}id bit + * needs to be dropped * @idmap: idmap of the mount @inode was found from * @inode: inode to check * @@ -165,7 +165,7 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry, unsigned int ia_valid = attr->ia_valid; /* - * First check size constraints. These can't be overriden using + * First check size constraints. These can't be overridden using * ATTR_FORCE. */ if (ia_valid & ATTR_SIZE) { From 97b67e64affb0e709eeecc50f6a9222fc20bd14b Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Wed, 1 Apr 2026 01:46:21 +1100 Subject: [PATCH 28/28] dcache: permit dynamic_dname()s up to NAME_MAX dynamic_dname() has had an implicit limit of 64 characters since it was introduced in commit c23fbb6bcb3e ("VFS: delay the dentry name generation on sockets and pipes"), however it seems that this was a fairly arbitrary number (suspiciously it was double the previously hardcoded buffer size). NAME_MAX seems like a more reasonable and consistent limit for d_name lengths. While we're at it, we can also remove the unnecessary stack-allocated array and just memmove() the formatted string to the end of the buffer. It should also be noted that at least one driver (in particular, liveupdate's usage of anon_inode for session files) already exceeded this limit without noticing that readlink(/proc/self/fd/$n) always returns -ENAMETOOLONG, so this fixes those drivers as well. Fixes: 0153094d03df ("liveupdate: luo_session: add sessions support") Fixes: c23fbb6bcb3e ("VFS: delay the dentry name generation on sockets and pipes") Signed-off-by: Aleksa Sarai Link: https://patch.msgid.link/20260401-dynamic-dname-name_max-v1-1-8ca20ab2642e@amutable.com Tested-by: Luca Boccassi Signed-off-by: Christian Brauner --- fs/d_path.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/d_path.c b/fs/d_path.c index bb365511066b..a48957c0971e 100644 --- a/fs/d_path.c +++ b/fs/d_path.c @@ -301,18 +301,19 @@ EXPORT_SYMBOL(d_path); char *dynamic_dname(char *buffer, int buflen, const char *fmt, ...) { va_list args; - char temp[64]; + char *start; int sz; va_start(args, fmt); - sz = vsnprintf(temp, sizeof(temp), fmt, args) + 1; + sz = vsnprintf(buffer, buflen, fmt, args) + 1; va_end(args); - if (sz > sizeof(temp) || sz > buflen) + if (sz > NAME_MAX || sz > buflen) return ERR_PTR(-ENAMETOOLONG); - buffer += buflen - sz; - return memcpy(buffer, temp, sz); + /* Move the formatted d_name to the end of the buffer. */ + start = buffer + (buflen - sz); + return memmove(start, buffer, sz); } char *simple_dname(struct dentry *dentry, char *buffer, int buflen)