lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <6965e980.050a0220.3be5c5.000f.GAE@google.com>
Date: Mon, 12 Jan 2026 22:43:12 -0800
From: syzbot <syzbot+6986a30df88382d1f7bf@...kaller.appspotmail.com>
To: linux-kernel@...r.kernel.org, syzkaller-bugs@...glegroups.com
Subject: Forwarded: [PATCH] ext4: add validation and bounds checking in ext4_read_inline_data()

For archival purposes, forwarding an incoming command email to
linux-kernel@...r.kernel.org, syzkaller-bugs@...glegroups.com.

***

Subject: [PATCH] ext4: add validation and bounds checking in ext4_read_inline_data()
Author: kartikey406@...il.com

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

Add comprehensive validation and bounds checking to prevent use-after-free
and out-of-bounds read vulnerabilities in ext4_read_inline_data().

The function previously trusted that:
1. The iloc buffer_head remained valid throughout execution
2. The xattr entry's e_value_offs pointed within valid inode bounds

This led to two critical vulnerabilities:

1. Use-after-free: The iloc->bh buffer could be freed by another thread
   between ext4_get_inode_loc() and ext4_read_inline_data(), causing
   reads from freed memory.

2. Out-of-bounds read: A corrupted or malicious filesystem image could
   set e_value_offs to point outside the inode's xattr area, causing
   reads beyond valid memory bounds.

This patch adds validation to:
- Check iloc and iloc->bh are non-NULL
- Verify buffer is still uptodate
- Validate xattr entry pointer is within inode bounds
- Bounds-check e_value_offs against xattr area size
- Ensure the final memcpy source and length stay within valid memory

Additionally, comprehensive debug logging has been added to aid in
diagnosing similar issues in the future.

The fix prevents both memory safety issues by catching invalid states
before the vulnerable memcpy operation at line 228.

Reported-by: syzbot+6986a30df88382d1f7bf@...kaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=6986a30df88382d1f7bf
Signed-off-by: Deepanshu Kartikey <kartikey406@...il.com>
---
 fs/ext4/inline.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 90 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 1f6bc05593df..f807598e96c9 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -188,16 +188,44 @@ static int ext4_read_inline_data(struct inode *inode, void *buffer,
 	struct ext4_xattr_ibody_header *header;
 	int cp_len = 0;
 	struct ext4_inode *raw_inode;
+	void *xattr_start, *xattr_end;
+	u32 value_offs;
+	void *source;
 
 	if (!len)
 		return 0;
 
+	printk(KERN_ERR "ext4_read_inline_data: START - inode=%lu, len=%u\n",
+	       inode->i_ino, len);
+
+	/* VALIDATION 1: Check iloc and buffer validity */
+	if (!iloc) {
+		printk(KERN_ERR "ext4_read_inline_data: ERROR - iloc is NULL\n");
+		return -EFSCORRUPTED;
+	}
+
+	if (!iloc->bh) {
+		printk(KERN_ERR "ext4_read_inline_data: ERROR - iloc->bh is NULL\n");
+		return -EFSCORRUPTED;
+	}
+
+	printk(KERN_ERR "ext4_read_inline_data: iloc->bh=%px, refcount=%d\n",
+	       iloc->bh, atomic_read(&iloc->bh->b_count));
+
+	if (!buffer_uptodate(iloc->bh)) {
+		printk(KERN_ERR "ext4_read_inline_data: ERROR - buffer not uptodate\n");
+		return -EIO;
+	}
+
 	BUG_ON(len > EXT4_I(inode)->i_inline_size);
 
 	cp_len = min_t(unsigned int, len, EXT4_MIN_INLINE_DATA_SIZE);
 
 	raw_inode = ext4_raw_inode(iloc);
+	printk(KERN_ERR "ext4_read_inline_data: raw_inode=%px\n", raw_inode);
+
 	memcpy(buffer, (void *)(raw_inode->i_block), cp_len);
+	printk(KERN_ERR "ext4_read_inline_data: copied %d bytes from i_block\n", cp_len);
 
 	len -= cp_len;
 	buffer += cp_len;
@@ -208,17 +236,77 @@ static int ext4_read_inline_data(struct inode *inode, void *buffer,
 	header = IHDR(inode, raw_inode);
 	entry = (struct ext4_xattr_entry *)((void *)raw_inode +
 					    EXT4_I(inode)->i_inline_off);
+
+	printk(KERN_ERR "ext4_read_inline_data: header=%px, entry=%px, i_inline_off=%u\n",
+	       header, entry, EXT4_I(inode)->i_inline_off);
+
+	/* VALIDATION 2: Calculate and check bounds */
+	xattr_start = (void *)IFIRST(header);
+	xattr_end = (void *)raw_inode + EXT4_INODE_SIZE(inode->i_sb);
+
+	printk(KERN_ERR "ext4_read_inline_data: BOUNDS - xattr_start=%px, xattr_end=%px, size=%ld\n",
+	       xattr_start, xattr_end, (long)(xattr_end - xattr_start));
+
+	/* VALIDATION 3: Check entry pointer is within inode */
+	if ((void *)entry < (void *)raw_inode || (void *)entry >= xattr_end) {
+		printk(KERN_ERR "ext4_read_inline_data: ERROR - entry %px outside inode bounds [%px-%px]\n",
+		       entry, raw_inode, xattr_end);
+		ext4_error_inode(inode, __func__, __LINE__, 0,
+				 "xattr entry outside inode bounds");
+		return -EFSCORRUPTED;
+	}
+
+	/* VALIDATION 4: Check e_value_offs */
+	value_offs = le16_to_cpu(entry->e_value_offs);
+	printk(KERN_ERR "ext4_read_inline_data: e_value_offs=%u, e_value_size=%u\n",
+	       value_offs, le32_to_cpu(entry->e_value_size));
+
+	if (value_offs > (xattr_end - xattr_start)) {
+		printk(KERN_ERR "ext4_read_inline_data: ERROR - value_offs %u exceeds xattr bounds %ld\n",
+		       value_offs, (long)(xattr_end - xattr_start));
+		ext4_error_inode(inode, __func__, __LINE__, 0,
+				 "corrupt xattr offset: %u", value_offs);
+		return -EFSCORRUPTED;
+	}
+
 	len = min_t(unsigned int, len,
 		    (unsigned int)le32_to_cpu(entry->e_value_size));
 
-	memcpy(buffer,
-	       (void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs), len);
+	/* VALIDATION 5: Check final read bounds */
+	source = xattr_start + value_offs;
+
+	printk(KERN_ERR "ext4_read_inline_data: FINAL CHECK - source=%px, len=%u, end=%px\n",
+	       source, len, source + len);
+
+	if (source < (void *)raw_inode) {
+		printk(KERN_ERR "ext4_read_inline_data: ERROR - source %px before inode start %px\n",
+		       source, raw_inode);
+		return -EFSCORRUPTED;
+	}
+
+	if ((source + len) > xattr_end) {
+		printk(KERN_ERR "ext4_read_inline_data: ERROR - read [%px-%px] exceeds inode end %px\n",
+		       source, source + len, xattr_end);
+		ext4_error_inode(inode, __func__, __LINE__, 0,
+				 "xattr read exceeds inode bounds");
+		return -EFSCORRUPTED;
+	}
+
+	printk(KERN_ERR "ext4_read_inline_data: ABOUT TO MEMCPY - source=%px, dest=%px, len=%u\n",
+	       source, buffer, len);
+
+	memcpy(buffer, source, len);
+
+	printk(KERN_ERR "ext4_read_inline_data: memcpy SUCCESS\n");
+
 	cp_len += len;
 
 out:
+	printk(KERN_ERR "ext4_read_inline_data: DONE - copied %d bytes total\n", cp_len);
 	return cp_len;
 }
 
+
 /*
  * write the buffer to the inline inode.
  * If 'create' is set, we don't need to do the extra copy in the xattr
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ