From 49f771d14578733a350c45e34e1bc6ad14f56952 Mon Sep 17 00:00:00 2001 From: Hui Peng Date: Wed, 21 Oct 2026 13:46:18 +0200 Subject: udf: validate vat20->lengthHeader and VAT inode size in udf_load_vat() In udf_load_vat() (fs/udf/super.c), validate that vat20->lengthHeader is at least sizeof(struct virtualAllocationTable20) and does not exceed the mapped buffer or VAT inode size so computing the VAT table pointer and entry count cannot underflow or read out of bounds. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://patch.msgid.link/20260919222600.3793513-1-benquike@gmail.com Signed-off-by: Hui Peng Signed-off-by: Jan Kara --- fs/udf/super.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/udf/super.c b/fs/udf/super.c index 5351755aca3e..2335c31cde31 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1252,6 +1252,12 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index) (sbi->s_vat_inode->i_size - 36) >> 2; } else if (map->s_partition_type == UDF_VIRTUAL_MAP20) { vati = UDF_I(sbi->s_vat_inode); + if (sbi->s_vat_inode->i_size < + sizeof(struct virtualAllocationTable20)) { + udf_err(sb, "Too short VAT inode size %lld\n", + sbi->s_vat_inode->i_size); + return -EFSCORRUPTED; + } if (vati->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { int err = 0; @@ -1269,8 +1275,12 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index) map->s_type_specific.s_virtual.s_start_offset = le16_to_cpu(vat20->lengthHeader); - if (map->s_type_specific.s_virtual.s_start_offset - > sbi->s_vat_inode->i_size) { + if (map->s_type_specific.s_virtual.s_start_offset < + sizeof(struct virtualAllocationTable20) || + map->s_type_specific.s_virtual.s_start_offset > + sb->s_blocksize || + map->s_type_specific.s_virtual.s_start_offset > + sbi->s_vat_inode->i_size) { udf_err(sb, "Corrupted VAT header length %u (VAT inode size %lld)\n", map->s_type_specific.s_virtual.s_start_offset, sbi->s_vat_inode->i_size); -- cgit v1.2.3 From e21a6f39aa6d0336ffbf5f34e5fa20ff8de36773 Mon Sep 17 00:00:00 2001 From: YouYou Yu Date: Wed, 21 Oct 2026 18:12:17 +0000 Subject: quota: initialize dquot cache before mounting rootfs On some systems, a tmpfs backed rootfs is extensively used throughout system's lifecycle. tmpfs quotas are needed to limit per user rootfs usage. However, booting with CONFIG_TMPFS_QUOTA=y, an initramfs, no root= and "initramfs_options=usrquota" panics at boot: BUG: kernel NULL pointer dereference, address: 0000000000000000 RIP: 0010:dqget+0x1ac/0x490 Call Trace: __dquot_initialize dquot_initialize shmem_get_inode shmem_fill_super ... mnt_init vfs_caches_init start_kernel The rootfs is mounted from mnt_init(), but the dquot hash table and cache are only set up later by dquot_init(), an fs_initcall. Creating the root inode of a quota enabled rootfs thus dereferences the NULL dquot_hash in dqget(). Rename dquot_init() to dquot_cache_init() and call it from vfs_caches_init() before mnt_init(). The slab, percpu and early workqueue code it relies on is available there, and the workqueue rescuer and shrinker debugfs entries are set up later by their own subsystems. Keep the sysctl registration in an fs_initcall, as done for the dcache, inode and mount sysctls. This only moves existing allocations earlier in boot. Fixes: 278033a225e1 ("fs: Add 'initramfs_options' to set initramfs mount options") Cc: stable@vger.kernel.org Assisted-by: LLM Co-developed-by: Dylan Johnson Signed-off-by: Dylan Johnson Signed-off-by: YouYou Yu Reviewed-by: Gunnar Kudrjavets Link: https://patch.msgid.link/20261021181217.20171-1-youyouyu@amazon.com Signed-off-by: Jan Kara --- fs/dcache.c | 2 ++ fs/quota/dquot.c | 10 ++++++---- include/linux/quotaops.h | 5 +++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 37d021d22a7d..a9a919bc9197 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "internal.h" #include "mount.h" @@ -3683,6 +3684,7 @@ void __init vfs_caches_init(void) inode_init(); files_init(); files_maxfiles_init(); + dquot_cache_init(); mnt_init(); bdev_cache_init(); chrdev_init(); diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 3ad19d7e472f..a3d503591871 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2958,7 +2958,7 @@ static const struct ctl_table fs_dqstats_table[] = { }, }; -static int __init dquot_init(void) +void __init dquot_cache_init(void) { int i, ret; unsigned long nr_hash; @@ -2966,8 +2966,6 @@ static int __init dquot_init(void) printk(KERN_NOTICE "VFS: Disk quotas %s\n", __DQUOT_VERSION__); - register_sysctl_init("fs/quota", fs_dqstats_table); - dquot_cachep = kmem_cache_create("dquot", sizeof(struct dquot), sizeof(unsigned long) * 4, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| @@ -3008,7 +3006,11 @@ static int __init dquot_init(void) WQ_UNBOUND | WQ_MEM_RECLAIM, WQ_MAX_ACTIVE); if (!quota_unbound_wq) panic("Cannot create quota_unbound_wq\n"); +} +static int __init init_fs_dquot_sysctls(void) +{ + register_sysctl_init("fs/quota", fs_dqstats_table); return 0; } -fs_initcall(dquot_init); +fs_initcall(init_fs_dquot_sysctls); diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 0c64ca674e77..7346efc85d43 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -40,6 +40,7 @@ void __quota_error(struct super_block *sb, const char *func, /* * declaration of quota_function calls in kernel. */ +void dquot_cache_init(void); int dquot_initialize(struct inode *inode); bool dquot_initialize_needed(struct inode *inode); void dquot_drop(struct inode *inode); @@ -206,6 +207,10 @@ static inline int sb_has_quota_active(struct super_block *sb, int type) return 0; } +static inline void dquot_cache_init(void) +{ +} + static inline int dquot_initialize(struct inode *inode) { return 0; -- cgit v1.2.3 From 0415c2624f8be6eac60de2b298de195dd18802d8 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 23 Oct 2026 10:41:21 +0200 Subject: udf: Validate extended attribute space is large enough When there is extended attributes space in ICB, there must be at least enough space for the header of this space. Otherwise the fs is corrupted and other offset validity checks could underflow. Reported-by: Lennart Finke Fixes: 127b0d87649f ("udf: Avoid confusion of missing and corrupted extended attributes") Signed-off-by: Jan Kara --- fs/udf/inode.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index bacf2a86161e..88b1c7d351e1 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1607,6 +1607,11 @@ reread: */ if (iinfo->i_lenEAttr > bs || iinfo->i_lenAlloc > bs) goto out; + /* We need to have space at least for generic descriptor */ + if (iinfo->i_lenEAttr && iinfo->i_lenEAttr < + sizeof(struct extendedAttrHeaderDesc)) + goto out; + /* Now do exact checks */ if (udf_file_entry_alloc_offset(inode) + iinfo->i_lenAlloc > bs) goto out; -- cgit v1.2.3 From afb28ca1f1ebe5279ffedd2fb24b92b8ee968351 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Fri, 23 Oct 2026 14:25:02 -0700 Subject: ext2: Make sbi an proper alias for EXT2_SB(sb) in ext2_fill_super() A recent LLVM change to fix parsing and validating __guarded_by on structure members [1] reveals a couple of warnings in ext2_fill_super() from the annotations added by commit 023051c2d0f1 ("ext2: mark s_mount_state as guarded by s_lock") and commit c23746524faf ("ext2: annotate s_rsv_window_root as requiring s_rsv_window_lock"): fs/ext2/super.c:1040:7: error: writing variable 's_mount_state' requires holding spinlock '_res->s_lock' exclusively [-Werror,-Wthread-safety-precise] 1040 | sbi->s_mount_state = le16_to_cpu(es->s_state); | ^ fs/ext2/super.c:1040:7: note: found near match 'EXT2_SB(sb).s_lock' fs/ext2/super.c:1138:8: error: writing variable 's_rsv_window_root' requires holding spinlock '_res->s_rsv_window_lock' exclusively [-Werror,-Wthread-safety-precise] 1138 | sbi->s_rsv_window_root = RB_ROOT; | ^ fs/ext2/super.c:1138:8: note: found near match 'EXT2_SB(sb).s_rsv_window_lock' Context analysis does very minimal alias detection [2], so it is not able to figure out that sbi == sb->s_fs_info == EXT2_SB(sb). The only reason there were no warnings before the above LLVM change is because the annotation was not parsed and verified correctly. Rework the allocation and assignment of sbi to have a direct assignment of EXT2_SB(sb) to sbi so that the analysis knows that sbi and EXT2_SB(sb) refer to the same object, clearing up the warnings. Fixes: 9708538ba204 ("ext2: enable context analysis support for ext2 filesystem") Link: https://github.com/llvm/llvm-project/commit/217e229a1f32785fccdd3b3f51429f0d309dea45 [1] Link: https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#no-alias-analysis [2] Signed-off-by: Nathan Chancellor Reviewed-by: Marco Elver Link: https://patch.msgid.link/20261023-ext2-fix-context-analysis-clang-24-v2-1-a33961453899@kernel.org Signed-off-by: Jan Kara --- fs/ext2/super.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/ext2/super.c b/fs/ext2/super.c index fb217ef2d69f..bde5b433776c 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -883,17 +883,18 @@ static int ext2_fill_super(struct super_block *sb, struct fs_context *fc) __le32 features; int err; - sbi = kzalloc_obj(*sbi); - if (!sbi) + sb->s_fs_info = kzalloc_obj(*sbi); + if (!sb->s_fs_info) return -ENOMEM; + sbi = EXT2_SB(sb); sbi->s_blockgroup_lock = kzalloc_obj(struct blockgroup_lock); if (!sbi->s_blockgroup_lock) { - kfree(sbi); + kfree(sb->s_fs_info); + sb->s_fs_info = NULL; return -ENOMEM; } - sb->s_fs_info = sbi; sbi->s_sb_block = sb_block; guard(spinlock_init)(&EXT2_SB(sb)->s_lock); -- cgit v1.2.3 From 5406ffab820ebad6637cca8737192449dcd680d5 Mon Sep 17 00:00:00 2001 From: Pedro Falcato Date: Sat, 24 Oct 2026 23:27:26 +0100 Subject: ext2: take invalidate_lock in ext2_write_failed() Currently, ext2_write_failed() takes care of removing extra data (folios & blocks) from the page cache by first truncating the page cache, then truncating the actual inode's blocks. Understanding that this is correct - namely, that a concurrent page cache lookup can't create stale pages and stale block mappings between truncate_pagecache() and ext2_truncate_blocks() - is non trivial. This is correct for two non-obvious reasons: 1) every caller of ext2_write_failed() holds some sort of exclusion against truncation. In most cases, callers hold (in some way) the inode lock. ext2_write_failed() is only reachable from buffered/direct IO write paths, which hold it. page_symlink() can also call write_begin _without the inode lock_, but at that point the inode is I_NEW, so there are no other users. 2) the page cache is generally extremely careful at never instantiating pages beyond EOF. This is all quite confusing. Holding the invalidate lock across both operations is largely simpler, and mirrors other bits of other filesystems (like ext4_truncate_failed_write). Do that. This has the side effect of making it so ext2_evict_inode() doesn't take the invalidate_lock when truncating blocks on inode deletion. This is safe as the inode should not have any users at that point. Signed-off-by: Pedro Falcato Link: https://patch.msgid.link/20261024222726.889520-1-pfalcato@suse.de Signed-off-by: Jan Kara --- fs/ext2/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 12ac4cfe1500..723b31dc7dac 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -58,8 +58,10 @@ void ext2_write_failed(struct address_space *mapping, loff_t to) struct inode *inode = mapping->host; if (to > inode->i_size) { + filemap_invalidate_lock(mapping); truncate_pagecache(inode, inode->i_size); ext2_truncate_blocks(inode, inode->i_size); + filemap_invalidate_unlock(mapping); } } @@ -1236,9 +1238,7 @@ static void ext2_truncate_blocks(struct inode *inode, loff_t offset) if (ext2_inode_is_fast_symlink(inode)) return; - filemap_invalidate_lock(inode->i_mapping); __ext2_truncate_blocks(inode, offset); - filemap_invalidate_unlock(inode->i_mapping); } static int ext2_setsize(struct inode *inode, loff_t newsize) -- cgit v1.2.3 From 1dcf2be94b9be2d366a0d2b70c2b1c7ebccf1443 Mon Sep 17 00:00:00 2001 From: Avi Weiss Date: Thu, 15 Oct 2026 20:57:01 +0300 Subject: fsnotify: preserve 64-bit pre-content ranges vfs_fallocate() and remap_verify_area() pass 64-bit lengths to fsnotify_file_area_perm(), which truncates them to size_t on 32-bit systems. Use 64 bit (unsigned) types throughout the entire pre-content notification chain to keep the full range. The userspace range field is already 64-bits. Fixes: 9740d17162de ("fsnotify: pass optional file access range in pre-content event") Signed-off-by: Avi Weiss Link: https://patch.msgid.link/20261015175701.3674449-1-thnkslprpt@gmail.com Signed-off-by: Jan Kara --- fs/notify/fanotify/fanotify.h | 2 +- fs/notify/fsnotify.c | 2 +- include/linux/fsnotify.h | 4 ++-- include/linux/fsnotify_backend.h | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 87ba788574bf..c25cf5c1fc70 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -444,7 +444,7 @@ struct fanotify_perm_event { struct fanotify_event fae; struct path path; loff_t pos; /* FANOTIFY_NO_RANGE if unavailable */ - size_t count; + u64 count; u32 response; /* userspace answer to the event */ unsigned short state; /* state of the event */ unsigned short watchdog_cnt; /* already scanned by watchdog? */ diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 7e2f330fd283..8657bb94dfa8 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -157,7 +157,7 @@ static inline __u32 fsnotify_object_watched(struct inode *inode, __u32 mnt_mask, /* Report pre-content event with optional range info */ int fsnotify_pre_content(const struct path *path, const loff_t *ppos, - size_t count) + u64 count) { struct file_range range; diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index bda798bc67bc..a4a29b365008 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -135,7 +135,7 @@ int fsnotify_open_perm_and_set_mode(struct file *file); * fsnotify_file_area_perm - permission hook before access to file range */ static inline int fsnotify_file_area_perm(struct file *file, int perm_mask, - const loff_t *ppos, size_t count) + const loff_t *ppos, u64 count) { /* * filesystem may be modified in the context of permission events @@ -213,7 +213,7 @@ static inline int fsnotify_open_perm_and_set_mode(struct file *file) } static inline int fsnotify_file_area_perm(struct file *file, int perm_mask, - const loff_t *ppos, size_t count) + const loff_t *ppos, u64 count) { return 0; } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index a6d5c1a89cff..faf21564380d 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -323,7 +323,7 @@ struct fs_error_report { struct file_range { const struct path *path; loff_t pos; - size_t count; + u64 count; }; static inline const struct path *file_range_path(const struct file_range *range) @@ -946,12 +946,12 @@ static inline void fsnotify_init_event(struct fsnotify_event *event) INIT_LIST_HEAD(&event->list); } int fsnotify_pre_content(const struct path *path, const loff_t *ppos, - size_t count); + u64 count); #else static inline int fsnotify_pre_content(const struct path *path, - const loff_t *ppos, size_t count) + const loff_t *ppos, u64 count) { return 0; } -- cgit v1.2.3