[PATCH openEuler-1.0-LTS 0/2] fix CVE-2026-89786 CVE-2026-89778
Xiang Mei (2): ext4: fix out-of-bounds read in ext4_read_inline_dir() isofs: fix out-of-bounds page array access on empty zisofs block fs/ext4/inline.c | 11 ++++++++++- fs/isofs/compress.c | 6 ++++-- 2 files changed, 14 insertions(+), 3 deletions(-) -- 2.34.1
From: Xiang Mei <xmei5@asu.edu> mainline inclusion from mainline-v7.3-rc1 commit 9333cc809f0a89e001b814155a6cb8903a6274df category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/19051 CVE: CVE-2026-89786 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- ext4_read_inline_dir() can read a dirent header past the end of its inline buffer, triggering a slab-out-of-bounds read during getdents64(): BUG: KASAN: slab-out-of-bounds in __ext4_check_dir_entry Read of size 2 at addr ffff88800f3dd23c by task exploit/148 ... __ext4_check_dir_entry ext4_read_inline_dir iterate_dir The dirent payload lives in a buffer of exactly inline_size bytes: dir_buf = kmalloc(inline_size, GFP_NOFS); but iteration runs in a position space extra_offset bytes larger (extra_size = extra_offset + inline_size) so the synthetic "." and ".." land at their block-dir offsets. A dirent is formed at "dir_buf + pos - extra_offset", yet the ext4_check_dir_entry() length argument uses the larger extra_size. A position whose dirent header would extend past extra_size is therefore accepted, and the rescan loop's rec_len probe and ext4_check_dir_entry() dereference de->rec_len before the entry is rejected. Reject a position whose minimum-size dirent header would not fit within extra_size before forming de, in both the rescan and main loops, and pass inline_size rather than extra_size to ext4_check_dir_entry() so the length check matches the physical buffer. Fixes: c4d8b0235aa9 ("ext4: fix readdir error in case inline_data+^dir_index.") Reported-by: Weiming Shi <bestswngs@gmail.com> Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei <xmei5@asu.edu> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260615190519.946736-1-xmei5@asu.edu Signed-off-by: Theodore Ts'o <tytso@mit.edu> Conflicts: fs/ext4/inline.c [ext4_dir_rec_len() does not exist in this 4.18 tree; use the equivalent EXT4_DIR_REC_LEN(1) macro for the dirent header size checks.] Co-authored-by: BackportAgent@deepseek-v4.1-flash Signed-off-by: Hulk Robot <hulkrobot@huawei.com> Signed-off-by: Ran Hongyun <ranhongyun1@huawei.com> Signed-off-by: Lin Yujun <linyujun809@h-partners.com> --- fs/ext4/inline.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 4e4d4291e0e1..7314b51b085a 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -1542,10 +1542,12 @@ int ext4_read_inline_dir(struct file *file, continue; } /* for other entry, the real offset in * the buf has to be tuned accordingly. */ + if (i + EXT4_DIR_REC_LEN(1) > extra_size) + break; de = (struct ext4_dir_entry_2 *) (dir_buf + i - extra_offset); /* It's too expensive to do a full * dirent test each time round this * loop, but we do have to test at @@ -1576,14 +1578,21 @@ int ext4_read_inline_dir(struct file *file, goto out; ctx->pos = dotdot_size; continue; } + /* + * de lives at dir_buf + ctx->pos - extra_offset, within the + * kmalloc(inline_size) buffer. Make sure its header fits before + * ext4_check_dir_entry() dereferences de->rec_len. + */ + if (ctx->pos + EXT4_DIR_REC_LEN(1) > extra_size) + goto out; de = (struct ext4_dir_entry_2 *) (dir_buf + ctx->pos - extra_offset); if (ext4_check_dir_entry(inode, file, de, iloc.bh, dir_buf, - extra_size, ctx->pos)) + inline_size, ctx->pos)) goto out; if (le32_to_cpu(de->inode)) { if (!dir_emit(ctx, de->name, de->name_len, le32_to_cpu(de->inode), get_dtype(sb, de->file_type))) -- 2.34.1
From: Xiang Mei <xmei5@asu.edu> mainline inclusion from mainline-v7.3-rc1 commit 68d4d3e78150c7ed7d1195af63ad1e6ace30c661 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/19048 CVE: CVE-2026-89778 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- zisofs_uncompress_block()'s empty-block fast path returns pcount << PAGE_SHIFT, ignoring the incoming poffset, unlike the decompression path which returns bytes produced relative to poffset. zisofs_fill_pages() uses that return to advance its page cursor, so when the zisofs block size is below PAGE_SIZE and a sub-page block leaves poffset partway into a page, a following empty block over-counts and advances pages[] one element past its end, after which "if (poffset && *pages)" reads pages[1] out of bounds. rock.c only rejects a block-size shift > 17, so a crafted "ZF" Rock Ridge record can set it below PAGE_SHIFT; the bug is reached by an ordinary read() of a compressed file on such a mounted ISO9660 image. Return the byte count relative to poffset and zero only [poffset, PAGE_SIZE) of the first page, matching the decompression path. The page-aligned case (poffset == 0) is unaffected. BUG: KASAN: slab-out-of-bounds in zisofs_read_folio (fs/isofs/compress.c:290) Read of size 8 at addr ffff88800f5eac48 by task exploit/142 zisofs_read_folio (fs/isofs/compress.c:290) read_pages (mm/readahead.c:184) ... filemap_read (mm/filemap.c:2814) vfs_read (fs/read_write.c:574) __x64_sys_pread64 (fs/read_write.c:769) do_syscall_64 (arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) The buggy address is located 0 bytes to the right of the allocated 8-byte region in the kmalloc-8 cache Fixes: 59bc055211b8 ("zisofs: Implement reading of compressed files when PAGE_CACHE_SIZE > compress block size") Reported-by: Weiming Shi <bestswngs@gmail.com> Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei <xmei5@asu.edu> Link: https://patch.msgid.link/20260712234150.3213467-1-xmei5@asu.edu Signed-off-by: Jan Kara <jack@suse.cz> Conflicts: fs/isofs/compress.c [memzero_page() does not exist in this tree, so use memset(page_address(pages[i]) + off, 0, PAGE_SIZE - off) followed by flush_dcache_page(), matching the pre-existing page clearing idiom here.] Co-authored-by: BackportAgent@deepseek-v4.1-flash Signed-off-by: Hulk Robot <hulkrobot@huawei.com> Signed-off-by: Ran Hongyun <ranhongyun1@huawei.com> Signed-off-by: Lin Yujun <linyujun809@h-partners.com> --- fs/isofs/compress.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/isofs/compress.c b/fs/isofs/compress.c index 10205ececc27..60f02e10dbef 100644 --- a/fs/isofs/compress.c +++ b/fs/isofs/compress.c @@ -68,17 +68,19 @@ static loff_t zisofs_uncompress_block(struct inode *inode, loff_t block_start, return 0; } /* Empty block? */ if (block_size == 0) { for ( i = 0 ; i < pcount ; i++ ) { + unsigned int off = i ? 0 : poffset; + if (!pages[i]) continue; - memset(page_address(pages[i]), 0, PAGE_SIZE); + memset(page_address(pages[i]) + off, 0, PAGE_SIZE - off); flush_dcache_page(pages[i]); SetPageUptodate(pages[i]); } - return ((loff_t)pcount) << PAGE_SHIFT; + return (((loff_t)pcount) << PAGE_SHIFT) - poffset; } /* Because zlib is not thread-safe, do all the I/O at the top. */ blocknum = block_start >> bufshift; bhs = kcalloc(needblocks + 1, sizeof(*bhs), GFP_KERNEL); -- 2.34.1
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/29434 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/DHT... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/29434 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/DHT...
participants (2)
-
Lin Yujun -
patchwork bot