[PATCH 1/3] resize2fs: out-of-bounds read/write in xattr entry scanning
Josh Hunt <[email protected]> Thu, 30 Jul 2026 20:36:59 -0700
| Newsgroups | org.kernel.vger.linux-ext4 |
|---|---|
| Message-ID | <[email protected]> |
From: Kit Knox <[email protected]> The fix_ea_entries() function iterates over extended attribute entries using the loop condition "while (entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry))". This check is insufficient because: 1. It does not verify the full 16-byte ext2_ext_attr_entry structure fits within the buffer before accessing its fields. With fewer than 16 bytes remaining, evaluating EXT2_EXT_IS_LAST_ENTRY() reads past the buffer. 2. A crafted e_name_len value can cause EXT2_EXT_ATTR_NEXT() to advance past the buffer boundary, and subsequent iterations read/write beyond allocated memory. 3. When entry->e_value_inum > last_ino, resize2fs writes to entry->e_value_inum, potentially corrupting heap memory. Additionally, fix_ea_ibody_entries() does not validate i_extra_isize before using it to compute xattr offsets. A malformed inode with an invalid i_extra_isize value (unaligned or too large) causes out-of-bounds reads when accessing the ea_magic field. Signed-off-by: Kit Knox <[email protected]> --- resize/resize2fs.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/resize/resize2fs.c b/resize/resize2fs.c index c8964af5..dfefca1d 100644 --- a/resize/resize2fs.c +++ b/resize/resize2fs.c @@ -2045,7 +2045,16 @@ static int fix_ea_entries(ext2_extent imap, struct ext2_ext_attr_entry *entry, int modified = 0; ext2_ino_t new_ino; - while (entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry)) { + /* + * Iterate over xattr entries with proper bounds checking. + * We must verify that the entire entry structure fits within + * the buffer before accessing any fields, as a malformed + * e_name_len could cause EXT2_EXT_ATTR_NEXT() to advance + * past the buffer boundary. + */ + while ((char *)entry + sizeof(struct ext2_ext_attr_entry) <= (char *)end && + (char *)EXT2_EXT_ATTR_NEXT(entry) <= (char *)end && + !EXT2_EXT_IS_LAST_ENTRY(entry)) { if (entry->e_value_inum > last_ino) { new_ino = ext2fs_extent_translate(imap, entry->e_value_inum); @@ -2063,10 +2072,20 @@ static int fix_ea_ibody_entries(ext2_extent imap, { struct ext2_ext_attr_entry *start, *end; __u32 *ea_magic; + int max_extra_isize; if (inode->i_extra_isize == 0) return 0; + /* + * Validate i_extra_isize before using it to compute offsets. + * It must be 4-byte aligned and fit within the inode. + */ + max_extra_isize = inode_size - EXT2_GOOD_OLD_INODE_SIZE; + if ((inode->i_extra_isize & 3) || + inode->i_extra_isize > max_extra_isize) + return 0; + ea_magic = (__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE + inode->i_extra_isize); if (*ea_magic != EXT2_EXT_ATTR_MAGIC) -- 2.34.1