Skip to content

Commit 5701875

Browse files
Ye Bintytso
authored andcommitted
ext4: fix out-of-bound read in ext4_xattr_inode_dec_ref_all()
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 <[email protected]> Reviewed-by: Jan Kara <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent 69f3a30 commit 5701875

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
@@ -4696,6 +4696,11 @@ static inline int ext4_iget_extra_inode(struct inode *inode,
46964696
*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) {
46974697
int err;
46984698

4699+
err = xattr_check_inode(inode, IHDR(inode, raw_inode),
4700+
ITAIL(inode, raw_inode));
4701+
if (err)
4702+
return err;
4703+
46994704
ext4_set_inode_state(inode, EXT4_STATE_XATTR);
47004705
err = ext4_find_inline_data_nolock(inode);
47014706
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
@@ -308,17 +308,14 @@ __ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh,
308308
__ext4_xattr_check_block((inode), (bh), __func__, __LINE__)
309309

310310

311-
static inline int
311+
int
312312
__xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
313313
void *end, const char *function, unsigned int line)
314314
{
315315
return check_xattrs(inode, NULL, IFIRST(header), end, IFIRST(header),
316316
function, line);
317317
}
318318

319-
#define xattr_check_inode(inode, header, end) \
320-
__xattr_check_inode((inode), (header), (end), __func__, __LINE__)
321-
322319
static int
323320
xattr_find_entry(struct inode *inode, struct ext4_xattr_entry **pentry,
324321
void *end, int name_index, const char *name, int sorted)
@@ -650,9 +647,6 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
650647
raw_inode = ext4_raw_inode(&iloc);
651648
header = IHDR(inode, raw_inode);
652649
end = ITAIL(inode, raw_inode);
653-
error = xattr_check_inode(inode, header, end);
654-
if (error)
655-
goto cleanup;
656650
entry = IFIRST(header);
657651
error = xattr_find_entry(inode, &entry, end, name_index, name, 0);
658652
if (error)
@@ -783,7 +777,6 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
783777
struct ext4_xattr_ibody_header *header;
784778
struct ext4_inode *raw_inode;
785779
struct ext4_iloc iloc;
786-
void *end;
787780
int error;
788781

789782
if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR))
@@ -793,14 +786,9 @@ ext4_xattr_ibody_list(struct dentry *dentry, char *buffer, size_t buffer_size)
793786
return error;
794787
raw_inode = ext4_raw_inode(&iloc);
795788
header = IHDR(inode, raw_inode);
796-
end = ITAIL(inode, raw_inode);
797-
error = xattr_check_inode(inode, header, end);
798-
if (error)
799-
goto cleanup;
800789
error = ext4_xattr_list_entries(dentry, IFIRST(header),
801790
buffer, buffer_size);
802791

803-
cleanup:
804792
brelse(iloc.bh);
805793
return error;
806794
}
@@ -868,7 +856,6 @@ int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)
868856
struct ext4_xattr_ibody_header *header;
869857
struct ext4_xattr_entry *entry;
870858
qsize_t ea_inode_refs = 0;
871-
void *end;
872859
int ret;
873860

874861
lockdep_assert_held_read(&EXT4_I(inode)->xattr_sem);
@@ -879,10 +866,6 @@ int ext4_get_inode_usage(struct inode *inode, qsize_t *usage)
879866
goto out;
880867
raw_inode = ext4_raw_inode(&iloc);
881868
header = IHDR(inode, raw_inode);
882-
end = ITAIL(inode, raw_inode);
883-
ret = xattr_check_inode(inode, header, end);
884-
if (ret)
885-
goto out;
886869

887870
for (entry = IFIRST(header); !IS_LAST_ENTRY(entry);
888871
entry = EXT4_XATTR_NEXT(entry))
@@ -2246,9 +2229,6 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
22462229
is->s.here = is->s.first;
22472230
is->s.end = ITAIL(inode, raw_inode);
22482231
if (ext4_test_inode_state(inode, EXT4_STATE_XATTR)) {
2249-
error = xattr_check_inode(inode, header, is->s.end);
2250-
if (error)
2251-
return error;
22522232
/* Find the named attribute. */
22532233
error = xattr_find_entry(inode, &is->s.here, is->s.end,
22542234
i->name_index, i->name, 0);
@@ -2799,10 +2779,6 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
27992779
min_offs = end - base;
28002780
total_ino = sizeof(struct ext4_xattr_ibody_header) + sizeof(u32);
28012781

2802-
error = xattr_check_inode(inode, header, end);
2803-
if (error)
2804-
goto cleanup;
2805-
28062782
ifree = ext4_xattr_free_space(base, &min_offs, base, &total_ino);
28072783
if (ifree >= isize_diff)
28082784
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)