CVE-2023-53143 Information
Description
In the Linux kernel the following vulnerability has been resolved:
ext4: fix another off-by-one fsmap error on 1k block filesystems
Apparently syzbot figured out that issuing this FSMAP call:
struct fsmap_head cmd = .fmh_count = …; .fmh_keys = .fmr_device = / ext4 dev / .fmr_physical = 0 .fmr_device = / ext4 dev / .fmr_physical = 0
… ; ret = ioctl(fd FS_IOC_GETFSMAP &cmd);
Produces this crash if the underlying filesystem is a 1k-block ext4 filesystem:
kernel BUG at fs/ext4/ext4.h:3331!
invalid opcode: 0000 [1] PREEMPT SMP
CPU: 3 PID: 3227965 Comm: xfs_io Tainted: G W O 6.2.0-rc8-achx
Hardware name: QEMU Standard PC (Q35 + ICH9 2009) BIOS 1.15.0-1 04/01/2014
RIP: 0010:ext4_mb_load_buddy_gfp+0x47c/0x570 [ext4]
RSP: 0018:ffffc90007c03998 EFLAGS: 00010246
RAX: ffff888004978000 RBX: ffffc90007c03a20 RCX: ffff888041618000
RDX: 0000000000000000 RSI: 00000000000005a4 RDI: ffffffffa0c99b11
RBP: ffff888012330000 R08: ffffffffa0c2b7d0 R09: 0000000000000400
R10: ffffc90007c03950 R11: 0000000000000000 R12: 0000000000000001
R13: 00000000ffffffff R14: 0000000000000c40 R15: ffff88802678c398
FS: 00007fdf2020c880(0000) GS:ffff88807e100000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ffd318a5fe8 CR3: 000000007f80f001 CR4: 00000000001706e0
Call Trace:
For GETFSMAP calls the caller selects a physical block device by writing its block number into fsmap_head.fmh_keys[01].fmr_device. To query mappings for a subrange of the device the starting byte of the range is written to fsmap_head.fmh_keys[0].fmr_physical and the last byte of the range goes in fsmap_head.fmh_keys[1].fmr_physical.
IOWs to query what mappings overlap with bytes 3-14 of /dev/sda you’d set the inputs as follows:
fmh_keys[0] = .fmr_device = major(8 0) .fmr_physical = 3
fmh_keys[1] = .fmr_device = major(8 0) .fmr_physical = 14
Which would return you whatever is mapped in the 12 bytes starting at physical offset 3.
The crash is due to insufficient range validation of keys[1] in ext4_getfsmap_datadev. On 1k-block filesystems block 0 is not part of the filesystem which means that s_first_data_block is nonzero. ext4_get_group_no_and_offset subtracts this quantity from the blocknr argument before cracking it into a group number and a block number within a group. IOWs block group 0 spans blocks 1-8192 (1-based) instead of 0-8191 (0-based) like what happens with larger blocksizes.
The net result of this encoding is that blocknr < s_first_data_block is not a valid input to this function. The end_fsb variable is set from the keys that are copied from userspace which means that in the above example its value is zero. That leads to an underflow here:
blocknr = blocknr - le32_to_cpu(es->s_first_data_block);
The division then operates on -1:
offset = do_div(blocknr EXT4_BLOCKS_PER_GROUP(sb)) >>
EXT4_SB(sb)->s_cluster_bits;
Leaving an impossibly large group number (2^32-1) in blocknr. ext4_getfsmap_check_keys checked that keys[0
truncated—
Reference
https://git.kernel.org/stable/c/15ebade3266b300da9cd1edce4004fe8fd6a2b88 https://git.kernel.org/stable/c/1d2366624b4c19a2ba6baf67fe57f4a1b0f67c05 https://git.kernel.org/stable/c/a70b49dc7eee5dbe3775a650ce598e3557ff5475 https://git.kernel.org/stable/c/c24f838493792b5e78a3596b4ca96375aa0af4c2 https://git.kernel.org/stable/c/c5d7c31e17224d847a330180ec1b03bf390632b2 https://git.kernel.org/stable/c/c993799baf9c5861f8df91beb80e1611b12efcbd https://git.kernel.org/stable/c/eb3a695aa71a514f2e7f5778e05faba3733b70a0 https://git.kernel.org/stable/c/f16054ac1774915160ca4e1c73ff7a269465a1b9
Share on: