From: Joseph Qi <joseph.qi@linux.alibaba.com>
To: ZhengYuan Huang <gality369@gmail.com>, akpm <akpm@linux-foundation.org>
Cc: ocfs2-devel@lists.linux.dev, linux-kernel@vger.kernel.org,
baijiaju1990@gmail.com, r33s3n6@gmail.com, zzzccc427@gmail.com,
Mark Fasheh <mark@fasheh.com>, Joel Becker <jlbec@evilplan.org>
Subject: Re: [PATCH 1/5] ocfs2: validate inline xattr header before ibody lookups
Date: Mon, 11 May 2026 14:26:09 +0800 [thread overview]
Message-ID: <5d1b0c85-fa4c-400c-b432-95260b311673@linux.alibaba.com> (raw)
In-Reply-To: <20260508085914.61647-2-gality369@gmail.com>
On 5/8/26 4:59 PM, ZhengYuan Huang wrote:
> [BUG]
> mknodat() can read past the end of a dinode block when ACL inheritance
> walks a corrupted inode-body xattr header. Another report shows the same
> unchecked lookup later faulting in the VFS open path after create
> returns a garbage status.
>
> KASAN: use-after-free in
> ocfs2_xattr_find_entry+0x37b/0x3a0 fs/ocfs2/xattr.c:1078
> Read of size 2 at addr ffff88801c520300 by task syz.0.10/360
>
> Trace:
> ...
> ocfs2_xattr_find_entry+0x37b/0x3a0 fs/ocfs2/xattr.c:1078
> ocfs2_xattr_ibody_get fs/ocfs2/xattr.c:1178 [inline]
> ocfs2_xattr_get_nolock+0x2ee/0x1110 fs/ocfs2/xattr.c:1309
> ocfs2_calc_xattr_init+0x716/0xac0 fs/ocfs2/xattr.c:628
> ocfs2_mknod+0x935/0x2400 fs/ocfs2/namei.c:333
> ocfs2_create+0x158/0x390 fs/ocfs2/namei.c:676
> vfs_create fs/namei.c:3493 [inline]
> vfs_create+0x445/0x6f0 fs/namei.c:3477
> do_mknodat+0x2d8/0x5e0 fs/namei.c:4372
> __do_sys_mknodat fs/namei.c:4400 [inline]
> __se_sys_mknodat fs/namei.c:4397 [inline]
> __x64_sys_mknodat+0xb6/0xf0 fs/namei.c:4397
> ...
>
> Another report:
> BUG: unable to handle page fault for address: fffffbfff3e40ec0
> RIP: 0010:__d_entry_type include/linux/dcache.h:414 [inline]
> RIP: 0010:d_can_lookup include/linux/dcache.h:429 [inline]
> RIP: 0010:d_is_dir include/linux/dcache.h:439 [inline]
> RIP: 0010:path_openat+0xe2f/0x2ce0 fs/namei.c:4134
>
> Trace:
> ...
> do_filp_open+0x1f6/0x430 fs/namei.c:4161
> do_sys_openat2+0x117/0x1c0 fs/open.c:1437
> __x64_sys_openat+0x15b/0x220 fs/open.c:1463
> ...
>
> [CAUSE]
> ocfs2_xattr_ibody_list() already validates the inline xattr size and
> entry count, but ocfs2_xattr_ibody_get() and ocfs2_xattr_ibody_find()
> still derive the inline header directly from di->i_xattr_inline_size and
> then trust xh_count. A corrupted inline size or entry count can therefore
> move the computed header outside the dinode block before get/find start
> walking it. That can either make ocfs2_xattr_find_entry() dereference
> xs->header->xh_count outside the block or make ocfs2_xattr_get_nolock()
> bubble a garbage status back through ocfs2_calc_xattr_init() into the
> create/open path.
>
> [FIX]
> Factor the existing ibody header geometry checks into a shared helper.
> Use it in ocfs2_xattr_ibody_get() and ocfs2_xattr_ibody_find(), and have
> ocfs2_xattr_ibody_list() reuse the same helper instead of open-coding
> the validation. Reject corrupt ibody metadata with -EFSCORRUPTED before
> the lookup path can walk bogus xattr geometry or return a garbage status.
>
> Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
Looks fine.
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
> ---
> fs/ocfs2/xattr.c | 82 +++++++++++++++++++++++++++---------------------
> 1 file changed, 47 insertions(+), 35 deletions(-)
>
> diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
> index 86cfd4c2adf9..3a5a17cdcf7e 100644
> --- a/fs/ocfs2/xattr.c
> +++ b/fs/ocfs2/xattr.c
> @@ -950,6 +950,41 @@ static int ocfs2_xattr_list_entries(struct inode *inode,
> return result;
> }
>
> +static int ocfs2_xattr_ibody_lookup_header(struct inode *inode,
> + struct ocfs2_dinode *di,
> + struct ocfs2_xattr_header **header)
> +{
> + u16 xattr_count;
> + size_t max_entries;
> + u16 inline_size = le16_to_cpu(di->i_xattr_inline_size);
> +
> + if (inline_size > inode->i_sb->s_blocksize ||
> + inline_size < sizeof(struct ocfs2_xattr_header)) {
> + ocfs2_error(inode->i_sb,
> + "Invalid xattr inline size %u in inode %llu\n",
> + inline_size,
> + (unsigned long long)OCFS2_I(inode)->ip_blkno);
> + return -EFSCORRUPTED;
> + }
> +
> + *header = (struct ocfs2_xattr_header *)
> + ((void *)di + inode->i_sb->s_blocksize - inline_size);
> +
> + xattr_count = le16_to_cpu((*header)->xh_count);
> + max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) /
> + sizeof(struct ocfs2_xattr_entry);
> +
> + if (xattr_count > max_entries) {
> + ocfs2_error(inode->i_sb,
> + "xattr entry count %u exceeds maximum %zu in inode %llu\n",
> + xattr_count, max_entries,
> + (unsigned long long)OCFS2_I(inode)->ip_blkno);
> + return -EFSCORRUPTED;
> + }
> +
> + return 0;
> +}
> +
> int ocfs2_has_inline_xattr_value_outside(struct inode *inode,
> struct ocfs2_dinode *di)
> {
> @@ -975,39 +1010,13 @@ static int ocfs2_xattr_ibody_list(struct inode *inode,
> struct ocfs2_xattr_header *header = NULL;
> struct ocfs2_inode_info *oi = OCFS2_I(inode);
> int ret = 0;
> - u16 xattr_count;
> - size_t max_entries;
> - u16 inline_size;
>
> if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
> return ret;
>
> - inline_size = le16_to_cpu(di->i_xattr_inline_size);
> -
> - /* Validate inline size is reasonable */
> - if (inline_size > inode->i_sb->s_blocksize ||
> - inline_size < sizeof(struct ocfs2_xattr_header)) {
> - ocfs2_error(inode->i_sb,
> - "Invalid xattr inline size %u in inode %llu\n",
> - inline_size,
> - (unsigned long long)OCFS2_I(inode)->ip_blkno);
> - return -EFSCORRUPTED;
> - }
> -
> - header = (struct ocfs2_xattr_header *)
> - ((void *)di + inode->i_sb->s_blocksize - inline_size);
> -
> - xattr_count = le16_to_cpu(header->xh_count);
> - max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) /
> - sizeof(struct ocfs2_xattr_entry);
> -
> - if (xattr_count > max_entries) {
> - ocfs2_error(inode->i_sb,
> - "xattr entry count %u exceeds maximum %zu in inode %llu\n",
> - xattr_count, max_entries,
> - (unsigned long long)OCFS2_I(inode)->ip_blkno);
> - return -EFSCORRUPTED;
> - }
> + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header);
> + if (ret)
> + return ret;
>
> ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size);
>
> @@ -1200,8 +1209,9 @@ static int ocfs2_xattr_ibody_get(struct inode *inode,
> return -ENODATA;
>
> xs->end = (void *)di + inode->i_sb->s_blocksize;
> - xs->header = (struct ocfs2_xattr_header *)
> - (xs->end - le16_to_cpu(di->i_xattr_inline_size));
> + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xs->header);
> + if (ret)
> + return ret;
> xs->base = (void *)xs->header;
> xs->here = xs->header->xh_entries;
>
> @@ -2726,12 +2736,14 @@ static int ocfs2_xattr_ibody_find(struct inode *inode,
>
> xs->xattr_bh = xs->inode_bh;
> xs->end = (void *)di + inode->i_sb->s_blocksize;
> - if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)
> - xs->header = (struct ocfs2_xattr_header *)
> - (xs->end - le16_to_cpu(di->i_xattr_inline_size));
> - else
> + if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) {
> + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xs->header);
> + if (ret)
> + return ret;
> + } else {
> xs->header = (struct ocfs2_xattr_header *)
> (xs->end - OCFS2_SB(inode->i_sb)->s_xattr_inline_size);
> + }
> xs->base = (void *)xs->header;
> xs->here = xs->header->xh_entries;
>
next prev parent reply other threads:[~2026-05-11 6:26 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-08 8:59 [PATCH 0/5] ocfs2: validate inline xattr header consumers ZhengYuan Huang
2026-05-08 8:59 ` [PATCH 1/5] ocfs2: validate inline xattr header before ibody lookups ZhengYuan Huang
2026-05-11 6:26 ` Joseph Qi [this message]
2026-05-08 8:59 ` [PATCH 2/5] ocfs2: validate inline xattr header before checking outside values ZhengYuan Huang
2026-05-11 6:30 ` Joseph Qi
2026-05-08 8:59 ` [PATCH 3/5] ocfs2: validate inline xattr header before ibody remove ZhengYuan Huang
2026-05-11 6:32 ` Joseph Qi
2026-05-08 8:59 ` [PATCH 4/5] ocfs2: validate inline xattr header before inline refcount attach ZhengYuan Huang
2026-05-11 6:33 ` Joseph Qi
2026-05-08 8:59 ` [PATCH 5/5] ocfs2: validate inline xattr header before reflinking inline xattrs ZhengYuan Huang
2026-05-11 6:35 ` Joseph Qi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5d1b0c85-fa4c-400c-b432-95260b311673@linux.alibaba.com \
--to=joseph.qi@linux.alibaba.com \
--cc=akpm@linux-foundation.org \
--cc=baijiaju1990@gmail.com \
--cc=gality369@gmail.com \
--cc=jlbec@evilplan.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mark@fasheh.com \
--cc=ocfs2-devel@lists.linux.dev \
--cc=r33s3n6@gmail.com \
--cc=zzzccc427@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox