The Linux Kernel Mailing List
 help / color / mirror / Atom feed
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;
>  


  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