On Wed 21-09-22 21:42:18, Baokun Li wrote: > If the starting position of our insert range happens to be in the hole > between the two ext4_extent_idx, because the lblk of the ext4_extent in > the previous ext4_extent_idx is always less than the start, which leads > to the "extent" variable access across the boundary, the following UAF is > triggered: > ================================================================== > BUG: KASAN: use-after-free in ext4_ext_shift_extents+0x257/0x790 > Read of size 4 at addr ffff88819807a008 by task fallocate/8010 > CPU: 3 PID: 8010 Comm: fallocate Tainted: G E 5.10.0+ #492 > Call Trace: > dump_stack+0x7d/0xa3 > print_address_description.constprop.0+0x1e/0x220 > kasan_report.cold+0x67/0x7f > ext4_ext_shift_extents+0x257/0x790 > ext4_insert_range+0x5b6/0x700 > ext4_fallocate+0x39e/0x3d0 > vfs_fallocate+0x26f/0x470 > ksys_fallocate+0x3a/0x70 > __x64_sys_fallocate+0x4f/0x60 > do_syscall_64+0x33/0x40 > entry_SYSCALL_64_after_hwframe+0x44/0xa9 > ================================================================== > > To solve this issue, when the ee_block of the last extent is less than > the start, exit the loop in advance to avoid UAF. > > Fixes: 331573febb6a ("ext4: Add support FALLOC_FL_INSERT_RANGE for fallocate") > Signed-off-by: Baokun Li <libaokun1@xxxxxxxxxx> > Signed-off-by: Zhihao Cheng <chengzhihao1@xxxxxxxxxx> Nice catch. The fix looks mostly good, just one small thing noted below. > diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c > index c148bb97b527..25fc1f4b35a5 100644 > --- a/fs/ext4/extents.c > +++ b/fs/ext4/extents.c > @@ -5216,11 +5216,18 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle, > } > > tmp = *iterator; > + extent = EXT_LAST_EXTENT(path[depth].p_hdr); > if (SHIFT == SHIFT_LEFT) { > - extent = EXT_LAST_EXTENT(path[depth].p_hdr); > *iterator = le32_to_cpu(extent->ee_block) + > ext4_ext_get_actual_len(extent); > } else { > + /* > + * start happens to be in the hole between > + * the two ext4_extent_idx. > + */ > + if (le32_to_cpu(extent->ee_block) < start) > + break; I think you need to initialize 'ret' somewhere (probably just after the again: label would make most sense) so that we don't accidentally return -EAGAIN here. > + > extent = EXT_FIRST_EXTENT(path[depth].p_hdr); > if (le32_to_cpu(extent->ee_block) > 0) > *iterator = le32_to_cpu(extent->ee_block) - 1; Honza -- Jan Kara <jack@xxxxxxxx> SUSE Labs, CR