Forwarded: [PATCH] ext4: fix bounds check in check_xattrs() to account for IS_LAST_ENTRY() read

syzbot posted 1 patch 1 week ago
fs/ext4/xattr.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Forwarded: [PATCH] ext4: fix bounds check in check_xattrs() to account for IS_LAST_ENTRY() read
Posted by syzbot 1 week ago
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] ext4: fix bounds check in check_xattrs() to account for IS_LAST_ENTRY() read
Author: kartikey406@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master


The bounds check for the next xattr entry in check_xattrs() uses
(void *)next >= end, which allows next to point within sizeof(u32)
bytes of end. On the next iteration, IS_LAST_ENTRY() reads 4 bytes
from next via *(__u32 *)(entry), which can overrun the valid xattr
region and access freed memory.

This is triggered when mounting a corrupted ext4 filesystem where the
inode's i_extra_isize leaves insufficient space for inline xattr
entries. The ACL permission check calls ext4_get_acl() ->
ext4_xattr_ibody_get() -> xattr_find_entry(), which reads past the
inode buffer.

Fix this by changing the check to (void *)next + sizeof(u32) > end,
ensuring there is always enough space for the IS_LAST_ENTRY() read
on the subsequent iteration.

Reported-by: syzbot+fb32afec111a7d61b939@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=fb32afec111a7d61b939
Link: https://lore.kernel.org/all/20260224231429.31361-1-kartikey406@gmail.com/T/ [v1]
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>

---
v2: Move the fix to check_xattrs() as suggested by Ted Ts'o, instead
    of adding a separate check in ext4_xattr_ibody_get().
---
 fs/ext4/xattr.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 7bf9ba19a89d..c6205b405efe 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -226,7 +226,7 @@ check_xattrs(struct inode *inode, struct buffer_head *bh,
 	/* Find the end of the names list */
 	while (!IS_LAST_ENTRY(e)) {
 		struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(e);
-		if ((void *)next >= end) {
+		if ((void *)next + sizeof(u32) > end) {
 			err_str = "e_name out of bounds";
 			goto errout;
 		}
-- 
2.43.0