* [PATCH 0/5] ocfs2: validate inline xattr header consumers
@ 2026-05-08 8:59 ZhengYuan Huang
2026-05-08 8:59 ` [PATCH 1/5] ocfs2: validate inline xattr header before ibody lookups ZhengYuan Huang
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: ZhengYuan Huang @ 2026-05-08 8:59 UTC (permalink / raw)
To: mark, jlbec, joseph.qi
Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
ZhengYuan Huang
Corrupt i_xattr_inline_size can move the computed inode-body xattr header
outside the dinode block. Several OCFS2 paths then trust xh_count or
xattr entry geometry from that unchecked header.
The reported KASAN splat hits the ibody lookup path:
BUG: KASAN: use-after-free in ocfs2_xattr_find_entry+0x37b/0x3a0
ocfs2_xattr_ibody_get()
ocfs2_xattr_get_nolock()
ocfs2_calc_xattr_init()
The same unchecked header derivation also exists in the outside-value
probe, ibody remove, inline refcount attach, and inline reflink paths.
This series factors the existing ibody list validation into a shared
helper and then converts the remaining inline-header consumers one at a
time.
Patch layout:
1. validate ibody get/find and reuse the helper in ibody list
2. validate the outside-value probe
3. validate ibody remove
4. validate inline refcount attach
5. validate inline reflink
ZhengYuan Huang (5):
ocfs2: validate inline xattr header before ibody lookups
ocfs2: validate inline xattr header before checking outside values
ocfs2: validate inline xattr header before ibody remove
ocfs2: validate inline xattr header before inline refcount attach
ocfs2: validate inline xattr header before reflinking inline xattrs
fs/ocfs2/xattr.c | 123 ++++++++++++++++++++++++++++-------------------
1 file changed, 73 insertions(+), 50 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/5] ocfs2: validate inline xattr header before ibody lookups
2026-05-08 8:59 [PATCH 0/5] ocfs2: validate inline xattr header consumers ZhengYuan Huang
@ 2026-05-08 8:59 ` ZhengYuan Huang
2026-05-11 6:26 ` Joseph Qi
2026-05-08 8:59 ` [PATCH 2/5] ocfs2: validate inline xattr header before checking outside values ZhengYuan Huang
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: ZhengYuan Huang @ 2026-05-08 8:59 UTC (permalink / raw)
To: mark, jlbec, joseph.qi
Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
ZhengYuan Huang
[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
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/5] ocfs2: validate inline xattr header before checking outside values
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-08 8:59 ` 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
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: ZhengYuan Huang @ 2026-05-08 8:59 UTC (permalink / raw)
To: mark, jlbec, joseph.qi
Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
ZhengYuan Huang
[BUG]
A corrupt inline xattr header can make
ocfs2_has_inline_xattr_value_outside() walk xh_count from an unchecked
header while refcount-tree teardown decides whether inline xattrs still
point outside the inode body.
[CAUSE]
ocfs2_has_inline_xattr_value_outside() still computed the inline header
directly from di->i_xattr_inline_size and immediately iterated xh_count.
That is the same unchecked metadata boundary as the ibody lookup bug.
[FIX]
Reuse the shared inline-header helper before iterating xh_count. Because
this helper returns a boolean-style answer to its caller, treat a corrupt
header conservatively as "has outside values" instead of walking it.
Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
---
fs/ocfs2/xattr.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 3a5a17cdcf7e..05f6f0a886cf 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -989,11 +989,12 @@ int ocfs2_has_inline_xattr_value_outside(struct inode *inode,
struct ocfs2_dinode *di)
{
struct ocfs2_xattr_header *xh;
+ int ret;
int i;
- xh = (struct ocfs2_xattr_header *)
- ((void *)di + inode->i_sb->s_blocksize -
- le16_to_cpu(di->i_xattr_inline_size));
+ ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xh);
+ if (ret)
+ return 1;
for (i = 0; i < le16_to_cpu(xh->xh_count); i++)
if (!ocfs2_xattr_is_local(&xh->xh_entries[i]))
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/5] ocfs2: validate inline xattr header before ibody remove
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-08 8:59 ` [PATCH 2/5] ocfs2: validate inline xattr header before checking outside values ZhengYuan Huang
@ 2026-05-08 8:59 ` 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-08 8:59 ` [PATCH 5/5] ocfs2: validate inline xattr header before reflinking inline xattrs ZhengYuan Huang
4 siblings, 1 reply; 11+ messages in thread
From: ZhengYuan Huang @ 2026-05-08 8:59 UTC (permalink / raw)
To: mark, jlbec, joseph.qi
Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
ZhengYuan Huang
[BUG]
A corrupt inline xattr header can make ocfs2_xattr_ibody_remove() pass an
unchecked header into ocfs2_remove_value_outside() during inode xattr
teardown.
[CAUSE]
ocfs2_xattr_ibody_remove() still rebuilt the ibody xattr header directly
from di->i_xattr_inline_size and then handed it to code that iterates
xh_count and entry geometry.
[FIX]
Validate the inline xattr header with the shared helper before handing it
to the outside-value removal path, and propagate -EFSCORRUPTED on bad
metadata instead of traversing the unchecked header.
Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
---
fs/ocfs2/xattr.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 05f6f0a886cf..bbb25a01b097 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -2476,9 +2476,9 @@ static int ocfs2_xattr_ibody_remove(struct inode *inode,
.vb_access = ocfs2_journal_access_di,
};
- header = (struct ocfs2_xattr_header *)
- ((void *)di + inode->i_sb->s_blocksize -
- le16_to_cpu(di->i_xattr_inline_size));
+ ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header);
+ if (ret)
+ return ret;
ret = ocfs2_remove_value_outside(inode, &vb, header,
ref_ci, ref_root_bh);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/5] ocfs2: validate inline xattr header before inline refcount attach
2026-05-08 8:59 [PATCH 0/5] ocfs2: validate inline xattr header consumers ZhengYuan Huang
` (2 preceding siblings ...)
2026-05-08 8:59 ` [PATCH 3/5] ocfs2: validate inline xattr header before ibody remove ZhengYuan Huang
@ 2026-05-08 8:59 ` 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
4 siblings, 1 reply; 11+ messages in thread
From: ZhengYuan Huang @ 2026-05-08 8:59 UTC (permalink / raw)
To: mark, jlbec, joseph.qi
Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
ZhengYuan Huang
[BUG]
A corrupt inline xattr header can make ocfs2_xattr_inline_attach_refcount()
feed an unchecked header into the refcount-attachment walk for inline
xattr values.
[CAUSE]
The inline refcount-attach path still derived the header directly from
di->i_xattr_inline_size and then passed it to code that iterates xh_count
and xattr entries.
[FIX]
Use the shared ibody header helper before attaching refcounts to inline
xattr values so corrupt header geometry is rejected with -EFSCORRUPTED
instead of being traversed.
Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
---
fs/ocfs2/xattr.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index bbb25a01b097..4877406a83ce 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -6016,14 +6016,17 @@ static int ocfs2_xattr_inline_attach_refcount(struct inode *inode,
struct ocfs2_cached_dealloc_ctxt *dealloc)
{
struct ocfs2_dinode *di = (struct ocfs2_dinode *)fe_bh->b_data;
- struct ocfs2_xattr_header *header = (struct ocfs2_xattr_header *)
- (fe_bh->b_data + inode->i_sb->s_blocksize -
- le16_to_cpu(di->i_xattr_inline_size));
+ struct ocfs2_xattr_header *header;
+ int ret;
struct ocfs2_xattr_value_buf vb = {
.vb_bh = fe_bh,
.vb_access = ocfs2_journal_access_di,
};
+ ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header);
+ if (ret)
+ return ret;
+
return ocfs2_xattr_attach_refcount_normal(inode, &vb, header,
ref_ci, ref_root_bh, dealloc);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 5/5] ocfs2: validate inline xattr header before reflinking inline xattrs
2026-05-08 8:59 [PATCH 0/5] ocfs2: validate inline xattr header consumers ZhengYuan Huang
` (3 preceding siblings ...)
2026-05-08 8:59 ` [PATCH 4/5] ocfs2: validate inline xattr header before inline refcount attach ZhengYuan Huang
@ 2026-05-08 8:59 ` ZhengYuan Huang
2026-05-11 6:35 ` Joseph Qi
4 siblings, 1 reply; 11+ messages in thread
From: ZhengYuan Huang @ 2026-05-08 8:59 UTC (permalink / raw)
To: mark, jlbec, joseph.qi
Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
ZhengYuan Huang
[BUG]
A corrupt inline xattr header can make ocfs2_reflink_xattr_inline() lock,
copy, and reflink xattr state from an unchecked ibody xattr header.
[CAUSE]
The inline reflink path still trusted di->i_xattr_inline_size to compute
header_off, xh, and new_xh before handing the source header to the reflink
allocator and copy logic.
[FIX]
Validate the source inode's inline xattr header with the shared helper
first, then derive the reflink copy offsets from the validated inline
size/header. This keeps the reflink path from traversing corrupt ibody
xattr geometry.
Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
---
fs/ocfs2/xattr.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 4877406a83ce..fcddd3c13acd 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -6511,12 +6511,10 @@ static int ocfs2_reflink_xattr_inline(struct ocfs2_xattr_reflink *args)
handle_t *handle;
struct ocfs2_super *osb = OCFS2_SB(args->old_inode->i_sb);
struct ocfs2_dinode *di = (struct ocfs2_dinode *)args->old_bh->b_data;
- int inline_size = le16_to_cpu(di->i_xattr_inline_size);
- int header_off = osb->sb->s_blocksize - inline_size;
- struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *)
- (args->old_bh->b_data + header_off);
- struct ocfs2_xattr_header *new_xh = (struct ocfs2_xattr_header *)
- (args->new_bh->b_data + header_off);
+ int inline_size;
+ int header_off;
+ struct ocfs2_xattr_header *xh;
+ struct ocfs2_xattr_header *new_xh;
struct ocfs2_alloc_context *meta_ac = NULL;
struct ocfs2_inode_info *new_oi;
struct ocfs2_dinode *new_di;
@@ -6525,6 +6523,15 @@ static int ocfs2_reflink_xattr_inline(struct ocfs2_xattr_reflink *args)
.vb_access = ocfs2_journal_access_di,
};
+ ret = ocfs2_xattr_ibody_lookup_header(args->old_inode, di, &xh);
+ if (ret)
+ goto out;
+
+ inline_size = le16_to_cpu(di->i_xattr_inline_size);
+ header_off = osb->sb->s_blocksize - inline_size;
+ new_xh = (struct ocfs2_xattr_header *)
+ (args->new_bh->b_data + header_off);
+
ret = ocfs2_reflink_lock_xattr_allocators(osb, xh, args->ref_root_bh,
&credits, &meta_ac);
if (ret) {
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 1/5] ocfs2: validate inline xattr header before ibody lookups
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
0 siblings, 0 replies; 11+ messages in thread
From: Joseph Qi @ 2026-05-11 6:26 UTC (permalink / raw)
To: ZhengYuan Huang, akpm
Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
Mark Fasheh, Joel Becker
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;
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/5] ocfs2: validate inline xattr header before checking outside values
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
0 siblings, 0 replies; 11+ messages in thread
From: Joseph Qi @ 2026-05-11 6:30 UTC (permalink / raw)
To: ZhengYuan Huang, akpm
Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
Mark Fasheh, Joel Becker
On 5/8/26 4:59 PM, ZhengYuan Huang wrote:
> [BUG]
> A corrupt inline xattr header can make
> ocfs2_has_inline_xattr_value_outside() walk xh_count from an unchecked
> header while refcount-tree teardown decides whether inline xattrs still
> point outside the inode body.
>
> [CAUSE]
> ocfs2_has_inline_xattr_value_outside() still computed the inline header
> directly from di->i_xattr_inline_size and immediately iterated xh_count.
> That is the same unchecked metadata boundary as the ibody lookup bug.
>
> [FIX]
> Reuse the shared inline-header helper before iterating xh_count. Because
> this helper returns a boolean-style answer to its caller, treat a corrupt
> header conservatively as "has outside values" instead of walking it.
>
> Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
Looks fine.
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
> ---
> fs/ocfs2/xattr.c | 7 ++++---
> 1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
> index 3a5a17cdcf7e..05f6f0a886cf 100644
> --- a/fs/ocfs2/xattr.c
> +++ b/fs/ocfs2/xattr.c
> @@ -989,11 +989,12 @@ int ocfs2_has_inline_xattr_value_outside(struct inode *inode,
> struct ocfs2_dinode *di)
> {
> struct ocfs2_xattr_header *xh;
> + int ret;
> int i;
>
> - xh = (struct ocfs2_xattr_header *)
> - ((void *)di + inode->i_sb->s_blocksize -
> - le16_to_cpu(di->i_xattr_inline_size));
> + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xh);
> + if (ret)
> + return 1;
>
> for (i = 0; i < le16_to_cpu(xh->xh_count); i++)
> if (!ocfs2_xattr_is_local(&xh->xh_entries[i]))
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/5] ocfs2: validate inline xattr header before ibody remove
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
0 siblings, 0 replies; 11+ messages in thread
From: Joseph Qi @ 2026-05-11 6:32 UTC (permalink / raw)
To: ZhengYuan Huang, akpm
Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
Mark Fasheh, Joel Becker
On 5/8/26 4:59 PM, ZhengYuan Huang wrote:
> [BUG]
> A corrupt inline xattr header can make ocfs2_xattr_ibody_remove() pass an
> unchecked header into ocfs2_remove_value_outside() during inode xattr
> teardown.
>
> [CAUSE]
> ocfs2_xattr_ibody_remove() still rebuilt the ibody xattr header directly
> from di->i_xattr_inline_size and then handed it to code that iterates
> xh_count and entry geometry.
>
> [FIX]
> Validate the inline xattr header with the shared helper before handing it
> to the outside-value removal path, and propagate -EFSCORRUPTED on bad
> metadata instead of traversing the unchecked header.
>
> Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
Looks fine.
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
> ---
> fs/ocfs2/xattr.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
> index 05f6f0a886cf..bbb25a01b097 100644
> --- a/fs/ocfs2/xattr.c
> +++ b/fs/ocfs2/xattr.c
> @@ -2476,9 +2476,9 @@ static int ocfs2_xattr_ibody_remove(struct inode *inode,
> .vb_access = ocfs2_journal_access_di,
> };
>
> - header = (struct ocfs2_xattr_header *)
> - ((void *)di + inode->i_sb->s_blocksize -
> - le16_to_cpu(di->i_xattr_inline_size));
> + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header);
> + if (ret)
> + return ret;
>
> ret = ocfs2_remove_value_outside(inode, &vb, header,
> ref_ci, ref_root_bh);
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/5] ocfs2: validate inline xattr header before inline refcount attach
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
0 siblings, 0 replies; 11+ messages in thread
From: Joseph Qi @ 2026-05-11 6:33 UTC (permalink / raw)
To: ZhengYuan Huang, akpm
Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
Mark Fasheh, Joel Becker
On 5/8/26 4:59 PM, ZhengYuan Huang wrote:
> [BUG]
> A corrupt inline xattr header can make ocfs2_xattr_inline_attach_refcount()
> feed an unchecked header into the refcount-attachment walk for inline
> xattr values.
>
> [CAUSE]
> The inline refcount-attach path still derived the header directly from
> di->i_xattr_inline_size and then passed it to code that iterates xh_count
> and xattr entries.
>
> [FIX]
> Use the shared ibody header helper before attaching refcounts to inline
> xattr values so corrupt header geometry is rejected with -EFSCORRUPTED
> instead of being traversed.
>
> Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
Looks fine.
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
> ---
> fs/ocfs2/xattr.c | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
> index bbb25a01b097..4877406a83ce 100644
> --- a/fs/ocfs2/xattr.c
> +++ b/fs/ocfs2/xattr.c
> @@ -6016,14 +6016,17 @@ static int ocfs2_xattr_inline_attach_refcount(struct inode *inode,
> struct ocfs2_cached_dealloc_ctxt *dealloc)
> {
> struct ocfs2_dinode *di = (struct ocfs2_dinode *)fe_bh->b_data;
> - struct ocfs2_xattr_header *header = (struct ocfs2_xattr_header *)
> - (fe_bh->b_data + inode->i_sb->s_blocksize -
> - le16_to_cpu(di->i_xattr_inline_size));
> + struct ocfs2_xattr_header *header;
> + int ret;
> struct ocfs2_xattr_value_buf vb = {
> .vb_bh = fe_bh,
> .vb_access = ocfs2_journal_access_di,
> };
>
> + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header);
> + if (ret)
> + return ret;
> +
> return ocfs2_xattr_attach_refcount_normal(inode, &vb, header,
> ref_ci, ref_root_bh, dealloc);
> }
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 5/5] ocfs2: validate inline xattr header before reflinking inline xattrs
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
0 siblings, 0 replies; 11+ messages in thread
From: Joseph Qi @ 2026-05-11 6:35 UTC (permalink / raw)
To: ZhengYuan Huang, akpm
Cc: ocfs2-devel, linux-kernel, baijiaju1990, r33s3n6, zzzccc427,
Mark Fasheh, Joel Becker
On 5/8/26 4:59 PM, ZhengYuan Huang wrote:
> [BUG]
> A corrupt inline xattr header can make ocfs2_reflink_xattr_inline() lock,
> copy, and reflink xattr state from an unchecked ibody xattr header.
>
> [CAUSE]
> The inline reflink path still trusted di->i_xattr_inline_size to compute
> header_off, xh, and new_xh before handing the source header to the reflink
> allocator and copy logic.
>
> [FIX]
> Validate the source inode's inline xattr header with the shared helper
> first, then derive the reflink copy offsets from the validated inline
> size/header. This keeps the reflink path from traversing corrupt ibody
> xattr geometry.
>
> Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
Looks fine.
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
> ---
> fs/ocfs2/xattr.c | 19 +++++++++++++------
> 1 file changed, 13 insertions(+), 6 deletions(-)
>
> diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
> index 4877406a83ce..fcddd3c13acd 100644
> --- a/fs/ocfs2/xattr.c
> +++ b/fs/ocfs2/xattr.c
> @@ -6511,12 +6511,10 @@ static int ocfs2_reflink_xattr_inline(struct ocfs2_xattr_reflink *args)
> handle_t *handle;
> struct ocfs2_super *osb = OCFS2_SB(args->old_inode->i_sb);
> struct ocfs2_dinode *di = (struct ocfs2_dinode *)args->old_bh->b_data;
> - int inline_size = le16_to_cpu(di->i_xattr_inline_size);
> - int header_off = osb->sb->s_blocksize - inline_size;
> - struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *)
> - (args->old_bh->b_data + header_off);
> - struct ocfs2_xattr_header *new_xh = (struct ocfs2_xattr_header *)
> - (args->new_bh->b_data + header_off);
> + int inline_size;
> + int header_off;
> + struct ocfs2_xattr_header *xh;
> + struct ocfs2_xattr_header *new_xh;
> struct ocfs2_alloc_context *meta_ac = NULL;
> struct ocfs2_inode_info *new_oi;
> struct ocfs2_dinode *new_di;
> @@ -6525,6 +6523,15 @@ static int ocfs2_reflink_xattr_inline(struct ocfs2_xattr_reflink *args)
> .vb_access = ocfs2_journal_access_di,
> };
>
> + ret = ocfs2_xattr_ibody_lookup_header(args->old_inode, di, &xh);
> + if (ret)
> + goto out;
> +
> + inline_size = le16_to_cpu(di->i_xattr_inline_size);
> + header_off = osb->sb->s_blocksize - inline_size;
> + new_xh = (struct ocfs2_xattr_header *)
> + (args->new_bh->b_data + header_off);
> +
> ret = ocfs2_reflink_lock_xattr_allocators(osb, xh, args->ref_root_bh,
> &credits, &meta_ac);
> if (ret) {
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-05-11 6:35 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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
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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox