Skip to content

Commit 098927a

Browse files
Ye Bingregkh
authored andcommitted
ext4: fix out-of-bound read in ext4_xattr_inode_dec_ref_all()
[ Upstream 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> Signed-off-by: Rajani Kantha <681739313@139.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 54f8f38 commit 098927a

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
@@ -4688,6 +4688,11 @@ static inline int ext4_iget_extra_inode(struct inode *inode,
46884688
*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) {
46894689
int err;
46904690

4691+
err = xattr_check_inode(inode, IHDR(inode, raw_inode),
4692+
ITAIL(inode, raw_inode));
4693+
if (err)
4694+
return err;
4695+
46914696
ext4_set_inode_state(inode, EXT4_STATE_XATTR);
46924697
err = ext4_find_inline_data_nolock(inode);
46934698
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
@@ -312,17 +312,14 @@ __ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh,
312312
__ext4_xattr_check_block((inode), (bh), __func__, __LINE__)
313313

314314

315-
static inline int
315+
int
316316
__xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
317317
void *end, const char *function, unsigned int line)
318318
{
319319
return check_xattrs(inode, NULL, IFIRST(header), end, IFIRST(header),
320320
function, line);
321321
}
322322

323-
#define xattr_check_inode(inode, header, end) \
324-
__xattr_check_inode((inode), (header), (end), __func__, __LINE__)
325-
326323
static int
327324
xattr_find_entry(struct inode *inode, struct ext4_xattr_entry **pentry,
328325
void *end, int name_index, const char *name, int sorted)
@@ -654,9 +651,6 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
654651
raw_inode = ext4_raw_inode(&iloc);
655652
header = IHDR(inode, raw_inode);
656653
end = ITAIL(inode, raw_inode);
657-
error = xattr_check_inode(inode, header, end);
658-
if (error)
659-
goto cleanup;
660654
entry = IFIRST(header);
661655
error = xattr_find_entry(inode, &entry, end, name_index, name, 0);
662656
if (error)
@@ -787,7 +781,6 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
787781
struct ext4_xattr_ibody_header *header;
788782
struct ext4_inode *raw_inode;
789783
struct ext4_iloc iloc;
790-
void *end;
791784
int error;
792785

793786
if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR))
@@ -797,14 +790,9 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
797790
return error;
798791
raw_inode = ext4_raw_inode(&iloc);
799792
header = IHDR(inode, raw_inode);
800-
end = ITAIL(inode, raw_inode);
801-
error = xattr_check_inode(inode, header, end);
802-
if (error)
803-
goto cleanup;
804793
error = ext4_xattr_list_entries(dentry, IFIRST(header),
805794
buffer, buffer_size);
806795

807-
cleanup:
808796
brelse(iloc.bh);
809797
return error;
810798
}
@@ -872,7 +860,6 @@ int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)
872860
struct ext4_xattr_ibody_header *header;
873861
struct ext4_xattr_entry *entry;
874862
qsize_t ea_inode_refs = 0;
875-
void *end;
876863
int ret;
877864

878865
lockdep_assert_held_read(&EXT4_I(inode)->xattr_sem);
@@ -883,10 +870,6 @@ int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)
883870
goto out;
884871
raw_inode = ext4_raw_inode(&iloc);
885872
header = IHDR(inode, raw_inode);
886-
end = ITAIL(inode, raw_inode);
887-
ret = xattr_check_inode(inode, header, end);
888-
if (ret)
889-
goto out;
890873

891874
for (entry = IFIRST(header); !IS_LAST_ENTRY(entry);
892875
entry = EXT4_XATTR_NEXT(entry))
@@ -2251,9 +2234,6 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
22512234
is->s.here = is->s.first;
22522235
is->s.end = ITAIL(inode, raw_inode);
22532236
if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
2254-
error = xattr_check_inode(inode, header, is->s.end);
2255-
if (error)
2256-
return error;
22572237
/* Find the named attribute. */
22582238
error = xattr_find_entry(inode, &is->s.here, is->s.end,
22592239
i->name_index, i->name, 0);
@@ -2804,10 +2784,6 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
28042784
min_offs = end - base;
28052785
total_ino = sizeof(struct ext4_xattr_ibody_header) + sizeof(u32);
28062786

2807-
error = xattr_check_inode(inode, header, end);
2808-
if (error)
2809-
goto cleanup;
2810-
28112787
ifree = ext4_xattr_free_space(base, &min_offs, base, &total_ino);
28122788
if (ifree >= isize_diff)
28132789
goto shift;

fs/ext4/xattr.h

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

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

0 commit comments

Comments
 (0)