Re: [PATCH] ocfs2: validate inline dir size in ocfs2_dir_foreach_blk_id

ZhengYuan Huang <[email protected]>
Newsgroups dev.linux.lists.ocfs2-devel,org.kernel.vger.linux-kernel
Message-ID <CAOmEq9X2YQpPu81nxnMj2Pr9KsVci7GqjvKf5-e2c4K8Pu-=Cg@mail.gmail.com>
On Fri, Apr 10, 2026 at 6:03 PM Joseph Qi <[email protected]> wrote:
> On 4/10/26 2:22 PM, ZhengYuan Huang wrote:
> > [BUG]
> > A crafted inline-data directory can set i_size larger than id_count.
> > readdir then walks past data->id_data and KASAN reports:
> >
> > BUG: KASAN: use-after-free in ocfs2_check_dir_entry.isra.0+0x31f/0x370 fs/ocfs2/dir.c:305
> > Read of size 2 at addr ffff8880088f0008 by task syz.0.1936/4656
> > Call Trace:
> >  ...
> >  ocfs2_check_dir_entry.isra.0+0x31f/0x370 fs/ocfs2/dir.c:305
> >  ocfs2_dir_foreach_blk_id+0x203/0xa70 fs/ocfs2/dir.c:1805
> >  ocfs2_dir_foreach_blk fs/ocfs2/dir.c:1933 [inline]
> >  ocfs2_readdir+0x4ba/0x520 fs/ocfs2/dir.c:1977
> >  wrap_directory_iterator+0x9c/0xe0 fs/readdir.c:65
> >  shared_ocfs2_readdir+0x29/0x40 fs/ocfs2/file.c:2822
> >  iterate_dir+0x276/0x9e0 fs/readdir.c:108
> >  __do_sys_getdents64 fs/readdir.c:410 [inline]
> >  __se_sys_getdents64 fs/readdir.c:396 [inline]
> >  __x64_sys_getdents64+0x143/0x2a0 fs/readdir.c:396
> >  ...
> >
> > [CAUSE]
> > ocfs2_dir_foreach_blk_id() uses i_size_read(inode) as the loop bound
> > after reading the inode block. Inline directories are only valid while
> > i_size <= le16_to_cpu(data->id_count), but that invariant is never
> > checked on read. Once ctx->pos reaches id_count, data->id_data +
> > ctx->pos points past the inode block and can land in a freed neighbor
> > page.
> >
>
> ocfs2_read_inode_block() has already validated dinode->i_size.
> So how it happens for the corrupted in-memory i_size?
>
> Thanks,
> Joseph

Thanks, my previous description was inaccurate.

The issue is not that inode->i_size becomes corrupted in memory after
ocfs2_read_inode_block() validates the dinode. The issue is that the
current dinode validation does not check the inline-directory
invariant i_size <= id_count.

ocfs2_validate_inode_block() does basic dinode validation, but it does
not verify that inline directory size fits within id_count. Then
ocfs2_populate_inode() / ocfs2_refresh_inode() /
ocfs2_refresh_inode_from_lvb() copy that unchecked size into the VFS
inode.

So the actual problem is an unchecked corrupted on-disk i_size, not a
post-validation in-memory corruption.

Once such an inode is loaded, readdir can legally reach
ocfs2_dir_foreach_blk_id(), which uses i_size_read(inode) as the upper
bound.

Based on your comment, I think the cleaner fix is to reject such
inline directories when the inode is loaded or refreshed, rather than
only checking in ocfs2_dir_foreach_blk_id(). I will respin the patch
accordingly.

Thanks,
ZhengYuan Huang
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.