Commit 2acb5c12 authored by Tejas Bharambe's avatar Tejas Bharambe Committed by Theodore Ts'o
Browse files

ext4: validate p_idx bounds in ext4_ext_correct_indexes



ext4_ext_correct_indexes() walks up the extent tree correcting
index entries when the first extent in a leaf is modified. Before
accessing path[k].p_idx->ei_block, there is no validation that
p_idx falls within the valid range of index entries for that
level.

If the on-disk extent header contains a corrupted or crafted
eh_entries value, p_idx can point past the end of the allocated
buffer, causing a slab-out-of-bounds read.

Fix this by validating path[k].p_idx against EXT_LAST_INDEX() at
both access sites: before the while loop and inside it. Return
-EFSCORRUPTED if the index pointer is out of range, consistent
with how other bounds violations are handled in the ext4 extent
tree code.

Reported-by: default avatar <syzbot+04c4e65cab786a2e5b7e@syzkaller.appspotmail.com>
Closes: https://syzkaller.appspot.com/bug?extid=04c4e65cab786a2e5b7e


Signed-off-by: default avatarTejas Bharambe <tejas.bharambe@outlook.com>
Link: https://patch.msgid.link/JH0PR06MB66326016F9B6AD24097D232B897CA@JH0PR06MB6632.apcprd06.prod.outlook.com


Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Cc: stable@kernel.org
parent 73bf12ad
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -1736,6 +1736,13 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
	err = ext4_ext_get_access(handle, inode, path + k);
	if (err)
		return err;
	if (unlikely(path[k].p_idx > EXT_LAST_INDEX(path[k].p_hdr))) {
		EXT4_ERROR_INODE(inode,
				 "path[%d].p_idx %p > EXT_LAST_INDEX %p",
				 k, path[k].p_idx,
				 EXT_LAST_INDEX(path[k].p_hdr));
		return -EFSCORRUPTED;
	}
	path[k].p_idx->ei_block = border;
	err = ext4_ext_dirty(handle, inode, path + k);
	if (err)
@@ -1748,6 +1755,14 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
		err = ext4_ext_get_access(handle, inode, path + k);
		if (err)
			goto clean;
		if (unlikely(path[k].p_idx > EXT_LAST_INDEX(path[k].p_hdr))) {
			EXT4_ERROR_INODE(inode,
					 "path[%d].p_idx %p > EXT_LAST_INDEX %p",
					 k, path[k].p_idx,
					 EXT_LAST_INDEX(path[k].p_hdr));
			err = -EFSCORRUPTED;
			goto clean;
		}
		path[k].p_idx->ei_block = border;
		err = ext4_ext_dirty(handle, inode, path + k);
		if (err)