Skip to content

Commit

Permalink
ext4: fix use-after-free in ext4_ext_shift_extents
Browse files Browse the repository at this point in the history
commit f6b1a1c upstream.

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+ torvalds#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
==================================================================

For right shifts, we can divide them into the following situations:

1. When the first ee_block of ext4_extent_idx is greater than or equal to
   start, make right shifts directly from the first ee_block.
    1) If it is greater than start, we need to continue searching in the
       previous ext4_extent_idx.
    2) If it is equal to start, we can exit the loop (iterator=NULL).

2. When the first ee_block of ext4_extent_idx is less than start, then
   traverse from the last extent to find the first extent whose ee_block
   is less than start.
    1) If extent is still the last extent after traversal, it means that
       the last ee_block of ext4_extent_idx is less than start, that is,
       start is located in the hole between idx and (idx+1), so we can
       exit the loop directly (break) without right shifts.
    2) Otherwise, make right shifts at the corresponding position of the
       found extent, and then exit the loop (iterator=NULL).

Fixes: 331573f ("ext4: Add support FALLOC_FL_INSERT_RANGE for fallocate")
Cc: [email protected] # v4.2+
Signed-off-by: Zhihao Cheng <[email protected]>
Signed-off-by: Baokun Li <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Theodore Ts'o <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
LiBaokun96 authored and Sasha Levin committed Nov 29, 2022
1 parent b2117ce commit 78180a5
Showing 1 changed file with 13 additions and 5 deletions.
18 changes: 13 additions & 5 deletions fs/ext4/extents.c
Original file line number Diff line number Diff line change
Expand Up @@ -5190,6 +5190,7 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
* and it is decreased till we reach start.
*/
again:
ret = 0;
if (SHIFT == SHIFT_LEFT)
iterator = &start;
else
Expand Down Expand Up @@ -5233,14 +5234,21 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
ext4_ext_get_actual_len(extent);
} else {
extent = EXT_FIRST_EXTENT(path[depth].p_hdr);
if (le32_to_cpu(extent->ee_block) > 0)
if (le32_to_cpu(extent->ee_block) > start)
*iterator = le32_to_cpu(extent->ee_block) - 1;
else
/* Beginning is reached, end of the loop */
else if (le32_to_cpu(extent->ee_block) == start)
iterator = NULL;
/* Update path extent in case we need to stop */
while (le32_to_cpu(extent->ee_block) < start)
else {
extent = EXT_LAST_EXTENT(path[depth].p_hdr);
while (le32_to_cpu(extent->ee_block) >= start)
extent--;

if (extent == EXT_LAST_EXTENT(path[depth].p_hdr))
break;

extent++;
iterator = NULL;
}
path[depth].p_ext = extent;
}
ret = ext4_ext_shift_path_extents(path, shift, inode,
Expand Down

0 comments on commit 78180a5

Please sign in to comment.