[PATCH] ext4: add bounds check in ext4_xattr_ibody_get() to prevent out-of-bounds access
From: Deepanshu Kartikey
Date: Tue Feb 24 2026 - 18:14:53 EST
When mounting a corrupted ext4 filesystem, the inode's i_extra_isize
can be set to a value that leaves insufficient space in the inode for
the inline xattr header and entries. While ext4_iget() validates that
i_extra_isize fits within the inode size, it does not account for the
additional sizeof(ext4_xattr_ibody_header) needed by IHDR/IFIRST.
This results in IFIRST(header) pointing at or beyond ITAIL(raw_inode),
leaving no room for even the 4-byte terminator entry. When
xattr_find_entry() is subsequently called, IS_LAST_ENTRY() reads 4
bytes from this out-of-bounds pointer, triggering a use-after-free.
For example, with EXT4_INODE_SIZE=256 and i_extra_isize=124:
- ext4_iget() check: 128 + 124 = 252 <= 256, passes
- IFIRST = offset 252 + 4 (xattr header) = 256
- ITAIL = 256
- IS_LAST_ENTRY() reads 4 bytes at offset 256, past the inode buffer
Fix this by validating in ext4_xattr_ibody_get() that there is enough
space between IFIRST(header) and ITAIL for at least a 4-byte read
before calling xattr_find_entry(). Return -EFSCORRUPTED if the inline
xattr region is too small.
Reported-by: syzbot+fb32afec111a7d61b939@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=fb32afec111a7d61b939
Tested-by: syzbot+fb32afec111a7d61b939@xxxxxxxxxxxxxxxxxxxxxxxxx
Signed-off-by: Deepanshu Kartikey <kartikey406@xxxxxxxxx>
---
fs/ext4/xattr.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 7bf9ba19a89d..5080ec44228a 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -652,6 +652,13 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
header = IHDR(inode, raw_inode);
end = ITAIL(inode, raw_inode);
entry = IFIRST(header);
+
+ if ((void *)entry + sizeof(__u32) > end) {
+ EXT4_ERROR_INODE(inode, "inline xattr region overflow");
+ error = -EFSCORRUPTED;
+ goto cleanup;
+ }
+
error = xattr_find_entry(inode, &entry, end, name_index, name, 0);
if (error)
goto cleanup;
--
2.34.1