Skip to content

Commit 62f6a66

Browse files
committed
ext4: fix out-of-bound read in ext4_xattr_inode_dec_ref_all()
jira VULN-65380 cve CVE-2025-22121 commit-author Ye Bin <yebin10@huawei.com> commit 5701875 There's issue as follows: BUG: KASAN: use-after-free in ext4_xattr_inode_dec_ref_all+0x6ff/0x790 Read of size 4 at addr ffff88807b003000 by task syz-executor.0/15172 CPU: 3 PID: 15172 Comm: syz-executor.0 Call Trace: __dump_stack lib/dump_stack.c:82 [inline] dump_stack+0xbe/0xfd lib/dump_stack.c:123 print_address_description.constprop.0+0x1e/0x280 mm/kasan/report.c:400 __kasan_report.cold+0x6c/0x84 mm/kasan/report.c:560 kasan_report+0x3a/0x50 mm/kasan/report.c:585 ext4_xattr_inode_dec_ref_all+0x6ff/0x790 fs/ext4/xattr.c:1137 ext4_xattr_delete_inode+0x4c7/0xda0 fs/ext4/xattr.c:2896 ext4_evict_inode+0xb3b/0x1670 fs/ext4/inode.c:323 evict+0x39f/0x880 fs/inode.c:622 iput_final fs/inode.c:1746 [inline] iput fs/inode.c:1772 [inline] iput+0x525/0x6c0 fs/inode.c:1758 ext4_orphan_cleanup fs/ext4/super.c:3298 [inline] ext4_fill_super+0x8c57/0xba40 fs/ext4/super.c:5300 mount_bdev+0x355/0x410 fs/super.c:1446 legacy_get_tree+0xfe/0x220 fs/fs_context.c:611 vfs_get_tree+0x8d/0x2f0 fs/super.c:1576 do_new_mount fs/namespace.c:2983 [inline] path_mount+0x119a/0x1ad0 fs/namespace.c:3316 do_mount+0xfc/0x110 fs/namespace.c:3329 __do_sys_mount fs/namespace.c:3540 [inline] __se_sys_mount+0x219/0x2e0 fs/namespace.c:3514 do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x67/0xd1 Memory state around the buggy address: ffff88807b002f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff88807b002f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff88807b003000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ^ ffff88807b003080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ffff88807b003100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff Above issue happens as ext4_xattr_delete_inode() isn't check xattr is valid if xattr is in inode. To solve above issue call xattr_check_inode() check if xattr if valid in inode. In fact, we can directly verify in ext4_iget_extra_inode(), so that there is no divergent verification. Fixes: e50e512 ("ext4: xattr-in-inode support") Signed-off-by: Ye Bin <yebin10@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20250208063141.1539283-3-yebin@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu> (cherry picked from commit 5701875) Signed-off-by: Brett Mastbergen <bmastbergen@ciq.com>
1 parent bee594b commit 62f6a66

File tree

3 files changed

+13
-25
lines changed

3 files changed

+13
-25
lines changed

fs/ext4/inode.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4685,6 +4685,11 @@ static inline int ext4_iget_extra_inode(struct inode *inode,
46854685
*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) {
46864686
int err;
46874687

4688+
err = xattr_check_inode(inode, IHDR(inode, raw_inode),
4689+
ITAIL(inode, raw_inode));
4690+
if (err)
4691+
return err;
4692+
46884693
ext4_set_inode_state(inode, EXT4_STATE_XATTR);
46894694
err = ext4_find_inline_data_nolock(inode);
46904695
if (!err && ext4_has_inline_data(inode))

fs/ext4/xattr.c

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -302,17 +302,14 @@ __ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh,
302302
__ext4_xattr_check_block((inode), (bh), __func__, __LINE__)
303303

304304

305-
static inline int
305+
int
306306
__xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
307307
void *end, const char *function, unsigned int line)
308308
{
309309
return check_xattrs(inode, NULL, IFIRST(header), end, IFIRST(header),
310310
function, line);
311311
}
312312

313-
#define xattr_check_inode(inode, header, end) \
314-
__xattr_check_inode((inode), (header), (end), __func__, __LINE__)
315-
316313
static int
317314
xattr_find_entry(struct inode *inode, struct ext4_xattr_entry **pentry,
318315
void *end, int name_index, const char *name, int sorted)
@@ -639,9 +636,6 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
639636
raw_inode = ext4_raw_inode(&iloc);
640637
header = IHDR(inode, raw_inode);
641638
end = ITAIL(inode, raw_inode);
642-
error = xattr_check_inode(inode, header, end);
643-
if (error)
644-
goto cleanup;
645639
entry = IFIRST(header);
646640
error = xattr_find_entry(inode, &entry, end, name_index, name, 0);
647641
if (error)
@@ -773,7 +767,6 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
773767
struct ext4_xattr_ibody_header *header;
774768
struct ext4_inode *raw_inode;
775769
struct ext4_iloc iloc;
776-
void *end;
777770
int error;
778771

779772
if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR))
@@ -783,14 +776,9 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
783776
return error;
784777
raw_inode = ext4_raw_inode(&iloc);
785778
header = IHDR(inode, raw_inode);
786-
end = ITAIL(inode, raw_inode);
787-
error = xattr_check_inode(inode, header, end);
788-
if (error)
789-
goto cleanup;
790779
error = ext4_xattr_list_entries(dentry, IFIRST(header),
791780
buffer, buffer_size);
792781

793-
cleanup:
794782
brelse(iloc.bh);
795783
return error;
796784
}
@@ -858,7 +846,6 @@ int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)
858846
struct ext4_xattr_ibody_header *header;
859847
struct ext4_xattr_entry *entry;
860848
qsize_t ea_inode_refs = 0;
861-
void *end;
862849
int ret;
863850

864851
lockdep_assert_held_read(&EXT4_I(inode)->xattr_sem);
@@ -869,10 +856,6 @@ int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)
869856
goto out;
870857
raw_inode = ext4_raw_inode(&iloc);
871858
header = IHDR(inode, raw_inode);
872-
end = ITAIL(inode, raw_inode);
873-
ret = xattr_check_inode(inode, header, end);
874-
if (ret)
875-
goto out;
876859

877860
for (entry = IFIRST(header); !IS_LAST_ENTRY(entry);
878861
entry = EXT4_XATTR_NEXT(entry))
@@ -2233,9 +2216,6 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
22332216
is->s.here = is->s.first;
22342217
is->s.end = ITAIL(inode, raw_inode);
22352218
if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
2236-
error = xattr_check_inode(inode, header, is->s.end);
2237-
if (error)
2238-
return error;
22392219
/* Find the named attribute. */
22402220
error = xattr_find_entry(inode, &is->s.here, is->s.end,
22412221
i->name_index, i->name, 0);
@@ -2752,10 +2732,6 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
27522732
min_offs = end - base;
27532733
total_ino = sizeof(struct ext4_xattr_ibody_header) + sizeof(u32);
27542734

2755-
error = xattr_check_inode(inode, header, end);
2756-
if (error)
2757-
goto cleanup;
2758-
27592735
ifree = ext4_xattr_free_space(base, &min_offs, base, &total_ino);
27602736
if (ifree >= isize_diff)
27612737
goto shift;

fs/ext4/xattr.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,13 @@ extern int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
210210
extern struct mb_cache *ext4_xattr_create_cache(void);
211211
extern void ext4_xattr_destroy_cache(struct mb_cache *);
212212

213+
extern int
214+
__xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
215+
void *end, const char *function, unsigned int line);
216+
217+
#define xattr_check_inode(inode, header, end) \
218+
__xattr_check_inode((inode), (header), (end), __func__, __LINE__)
219+
213220
#ifdef CONFIG_EXT4_FS_SECURITY
214221
extern int ext4_init_security(handle_t *handle, struct inode *inode,
215222
struct inode *dir, const struct qstr *qstr);

0 commit comments

Comments
 (0)