From: ZhengYuan Huang <gality369@gmail.com>
To: mark@fasheh.com, jlbec@evilplan.org, joseph.qi@linux.alibaba.com
Cc: ocfs2-devel@lists.linux.dev, linux-kernel@vger.kernel.org,
baijiaju1990@gmail.com, r33s3n6@gmail.com, zzzccc427@gmail.com,
ZhengYuan Huang <gality369@gmail.com>
Subject: [PATCH 1/5] ocfs2: validate inline xattr header before ibody lookups
Date: Fri, 8 May 2026 16:59:10 +0800 [thread overview]
Message-ID: <20260508085914.61647-2-gality369@gmail.com> (raw)
In-Reply-To: <20260508085914.61647-1-gality369@gmail.com>
[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>
---
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;
--
2.43.0
next prev parent reply other threads:[~2026-05-08 8:59 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 ` ZhengYuan Huang [this message]
2026-05-11 6:26 ` [PATCH 1/5] ocfs2: validate inline xattr header before ibody lookups Joseph Qi
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=20260508085914.61647-2-gality369@gmail.com \
--to=gality369@gmail.com \
--cc=baijiaju1990@gmail.com \
--cc=jlbec@evilplan.org \
--cc=joseph.qi@linux.alibaba.com \
--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