From nobody Sat Feb 7 13:41:13 2026 Received: from mail-ot1-f71.google.com (mail-ot1-f71.google.com [209.85.210.71]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F150025F98B for ; Tue, 13 Jan 2026 06:43:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.71 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768286595; cv=none; b=bxIxCRukB0LgEF0/x8a4tjwWSOJK/XrfTI0mGr6fEHJy2nVgRY6ysCf2lO88/Qwh5k5Mc1pCXnQBr2s7BP4mgw0smsJmYCKtGt9xU1rIQjinkPz1+hm5n9l75+Bc+pVHT5QaA4mh8d2ON0BwB1vGOg8B1MuZcOlwoIC0hRTAep8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768286595; c=relaxed/simple; bh=ia2gu7uhw97HPdK0AdTKjTp5pnOJUnPT2CIr1jjvfks=; h=MIME-Version:Date:In-Reply-To:Message-ID:Subject:From:To: Content-Type; b=SCBXQeb7pcH3GFqTOkihgWNDpOoGONpIg8UlFNMY+AnSa5RkafruXOC8ljrkvzTHkiG+4LRzrqBjqI50ggPZ3W2ERxYB4mbi0+mKPyLdP+6HVM3dJkhx/y0bL988Kxvn12PmHcR7JIAyemZj7ETVHPd/h3W+8wCdBg+CirFLkl0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=syzkaller.appspotmail.com; spf=pass smtp.mailfrom=M3KW2WVRGUFZ5GODRSRYTGD7.apphosting.bounces.google.com; arc=none smtp.client-ip=209.85.210.71 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=syzkaller.appspotmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=M3KW2WVRGUFZ5GODRSRYTGD7.apphosting.bounces.google.com Received: by mail-ot1-f71.google.com with SMTP id 46e09a7af769-7c6ce3b9fa0so9269248a34.0 for ; Mon, 12 Jan 2026 22:43:13 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768286593; x=1768891393; h=to:from:subject:message-id:in-reply-to:date:mime-version :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=+nZ48FGaRFUL10YgZuSaMtfHOFuLVHx9JM92FRKH0+k=; b=jKdlkL4U7p20Cyc4eSg2EXYoes5vSlugsHc+9U2HM3F+oP/Uc72C19fYsX5PiYYKyM m9EDIQPS137mEfxkWpDhyKA9G1lLknT/vrtqkmfx0h46tFjD90Feh7Du3KSVulJNLGdp aGitS+dgSr+hOsMx0cs2xzMzxb5BqXws7+Q1OTn99LyswPmnSiOMALv9vxtQSWePJjpF BHiSp6Ga4m7vngosjBvpNy8PuGH8Q1YbvFCprbMcf7KhXtKy5DYAqjT1PROVNIyREW2w rMPueCl0cJMeViyj0jmqCffyNR2MDQOSJSg707BugJfp2UzN7eERIyRr0VVbc4MyQ43W +Rcw== X-Gm-Message-State: AOJu0Yyu9fX04Okv2XViFQnnzGXym9xb8BRdMO68vaUZfU56mYfa/w67 f6gz37e1FQ5Shu7tILvH92+m/CHVTFy/lWq2oDvmHbHuDQy/0khL7xQuAr70vjgiJYdhmXv8C5a 1h+84jG9PbQogJ9AbvGYcSSWoTvJyXfsG9u2dAj1lZhjKaAP4zahXxcBHbbc= X-Google-Smtp-Source: AGHT+IFW8Ns5W4pf3zZuLB48/h/vGAk41eqHo20Qf/hwoBvljBTbFJCUJBcMaDjyyDb1cQ51LBmFE5WwoGnUmI+9uMzD5gcDp0w+ Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Received: by 2002:a05:6820:6601:b0:65b:3c3b:69b7 with SMTP id 006d021491bc7-65f54f5ab80mr5696506eaf.39.1768286592935; Mon, 12 Jan 2026 22:43:12 -0800 (PST) Date: Mon, 12 Jan 2026 22:43:12 -0800 In-Reply-To: <6965a06e.050a0220.38aacd.0005.GAE@google.com> X-Google-Appengine-App-Id: s~syzkaller X-Google-Appengine-App-Id-Alias: syzkaller Message-ID: <6965e980.050a0220.3be5c5.000f.GAE@google.com> Subject: Forwarded: [PATCH] ext4: add validation and bounds checking in ext4_read_inline_data() From: syzbot To: linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" For archival purposes, forwarding an incoming command email to linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com. *** Subject: [PATCH] ext4: add validation and bounds checking in ext4_read_inli= ne_data() Author: kartikey406@gmail.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@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3D6986a30df88382d1f7bf Signed-off-by: Deepanshu Kartikey --- 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 =3D 0; struct ext4_inode *raw_inode; + void *xattr_start, *xattr_end; + u32 value_offs; + void *source; =20 if (!len) return 0; =20 + printk(KERN_ERR "ext4_read_inline_data: START - inode=3D%lu, len=3D%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=3D%px, refcount=3D%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); =20 cp_len =3D min_t(unsigned int, len, EXT4_MIN_INLINE_DATA_SIZE); =20 raw_inode =3D ext4_raw_inode(iloc); + printk(KERN_ERR "ext4_read_inline_data: raw_inode=3D%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); =20 len -=3D cp_len; buffer +=3D cp_len; @@ -208,17 +236,77 @@ static int ext4_read_inline_data(struct inode *inode,= void *buffer, header =3D IHDR(inode, raw_inode); entry =3D (struct ext4_xattr_entry *)((void *)raw_inode + EXT4_I(inode)->i_inline_off); + + printk(KERN_ERR "ext4_read_inline_data: header=3D%px, entry=3D%px, i_inli= ne_off=3D%u\n", + header, entry, EXT4_I(inode)->i_inline_off); + + /* VALIDATION 2: Calculate and check bounds */ + xattr_start =3D (void *)IFIRST(header); + xattr_end =3D (void *)raw_inode + EXT4_INODE_SIZE(inode->i_sb); + + printk(KERN_ERR "ext4_read_inline_data: BOUNDS - xattr_start=3D%px, xattr= _end=3D%px, size=3D%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 >=3D 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 =3D le16_to_cpu(entry->e_value_offs); + printk(KERN_ERR "ext4_read_inline_data: e_value_offs=3D%u, e_value_size= =3D%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 xa= ttr 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 =3D min_t(unsigned int, len, (unsigned int)le32_to_cpu(entry->e_value_size)); =20 - memcpy(buffer, - (void *)IFIRST(header) + le16_to_cpu(entry->e_value_offs), len); + /* VALIDATION 5: Check final read bounds */ + source =3D xattr_start + value_offs; + + printk(KERN_ERR "ext4_read_inline_data: FINAL CHECK - source=3D%px, len= =3D%u, end=3D%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 i= node 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=3D%px, d= est=3D%px, len=3D%u\n", + source, buffer, len); + + memcpy(buffer, source, len); + + printk(KERN_ERR "ext4_read_inline_data: memcpy SUCCESS\n"); + cp_len +=3D len; =20 out: + printk(KERN_ERR "ext4_read_inline_data: DONE - copied %d bytes total\n", = cp_len); return cp_len; } =20 + /* * write the buffer to the inline inode. * If 'create' is set, we don't need to do the extra copy in the xattr --=20 2.43.0