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] ocfs2: validate local xattr value bounds on lookup
Date: Wed, 29 Apr 2026 10:20:05 +0800 [thread overview]
Message-ID: <20260429022005.1461600-1-gality369@gmail.com> (raw)
[BUG]
A corrupted local xattr entry can crash ACL lookup during open.
BUG: KASAN: use-after-free in ocfs2_xattr_block_get fs/ocfs2/xattr.c:1261 [inline]
BUG: KASAN: use-after-free in ocfs2_xattr_get_nolock+0x8cb/0x1110 fs/ocfs2/xattr.c:1312
Read of size 252 at addr ffff88801e7dbfb8 by task syz.0.1/347
Call Trace:
...
ocfs2_xattr_block_get fs/ocfs2/xattr.c:1261 [inline]
ocfs2_xattr_get_nolock+0x8cb/0x1110 fs/ocfs2/xattr.c:1312
ocfs2_get_acl_nolock+0xb1/0x3d0 fs/ocfs2/acl.c:137
ocfs2_iop_get_acl+0x197/0x260 fs/ocfs2/acl.c:313
__get_acl+0x33a/0x480 fs/posix_acl.c:159
get_inode_acl+0x2b/0x40 fs/posix_acl.c:184
check_acl fs/namei.c:333 [inline]
acl_permission_check fs/namei.c:419 [inline]
generic_permission+0x509/0x670 fs/namei.c:472
ocfs2_permission+0xeb/0x1a0 fs/ocfs2/file.c:1366
do_inode_permission fs/namei.c:526 [inline]
inode_permission+0x317/0x5a0 fs/namei.c:593
may_open+0x10e/0x350 fs/namei.c:3565
do_open fs/namei.c:3973 [inline]
path_openat+0x500/0x2ce0 fs/namei.c:4134
do_filp_open+0x1f6/0x430 fs/namei.c:4161
do_sys_openat2+0x117/0x1c0 fs/open.c:1437
do_sys_open fs/open.c:1452 [inline]
__do_sys_open fs/open.c:1460 [inline]
__se_sys_open fs/open.c:1456 [inline]
__x64_sys_open+0x12d/0x1e0 fs/open.c:1456
...
[CAUSE]
The lookup helpers only validate the entry position and the name bytes.
Once a name matches, ocfs2_xattr_block_get() trusts xe_value_size and
memcpy()s that many bytes from inline xattr storage.
A local xattr entry is only valid when its inline value both fits the
local size limit and stays inside the current xattr block or bucket
block. That invariant is never checked for matched local entries, so
corrupted metadata can inflate xe_value_size and drive memcpy() past the
end of the source block.
[FIX]
Validate matched local xattr entries before returning them from
ocfs2_xattr_find_entry() and ocfs2_find_xe_in_bucket(). Reject entries
whose inline value exceeds the local limit or crosses the current block
boundary, and route the failure through normal OCFS2 corruption
handling.
Signed-off-by: ZhengYuan Huang <gality369@gmail.com>
---
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -237,6 +237,29 @@
return namevalue_size(xe->xe_name_len, value_len);
}
+static int ocfs2_validate_local_xe(struct inode *inode,
+ struct ocfs2_xattr_entry *xe,
+ void *base,
+ void *end)
+{
+ char *nameval = base + le16_to_cpu(xe->xe_name_offset);
+ size_t name_len = OCFS2_XATTR_SIZE(xe->xe_name_len);
+ u64 value_size = le64_to_cpu(xe->xe_value_size);
+
+ if (value_size > OCFS2_XATTR_INLINE_SIZE)
+ return ocfs2_error(inode->i_sb,
+ "Inode %llu has invalid local xattr value size %llu\n",
+ (unsigned long long)OCFS2_I(inode)->ip_blkno,
+ (unsigned long long)value_size);
+
+ if (nameval + name_len + value_size > (char *)end)
+ return ocfs2_error(inode->i_sb,
+ "Inode %llu has corrupt local xattr value bounds\n",
+ (unsigned long long)OCFS2_I(inode)->ip_blkno);
+
+ return 0;
+}
+
static int ocfs2_xattr_bucket_get_name_value(struct super_block *sb,
struct ocfs2_xattr_header *xh,
@@ -1098,7 +1121,7 @@
{
struct ocfs2_xattr_entry *entry;
size_t name_len;
- int i, name_offset, cmp = 1;
+ int i, name_offset, cmp = 1, ret;
if (name == NULL)
return -EINVAL;
@@ -1121,6 +1144,11 @@
return -EFSCORRUPTED;
}
cmp = memcmp(name, (xs->base + name_offset), name_len);
+ if (!cmp && ocfs2_xattr_is_local(entry)) {
+ ret = ocfs2_validate_local_xe(inode, entry, xs->base, xs->end);
+ if (ret)
+ return ret;
+ }
}
if (cmp == 0)
break;
@@ -3790,6 +3818,8 @@
struct ocfs2_xattr_header *xh = bucket_xh(bucket);
size_t name_len = strlen(name);
struct ocfs2_xattr_entry *xe = NULL;
+ char *block;
+ char *block_end;
char *xe_name;
/*
@@ -3821,8 +3851,15 @@
}
- xe_name = bucket_block(bucket, block_off) + new_offset;
+ block = bucket_block(bucket, block_off);
+ block_end = block + inode->i_sb->s_blocksize;
+ xe_name = block + new_offset;
if (!memcmp(name, xe_name, name_len)) {
+ if (ocfs2_xattr_is_local(xe)) {
+ ret = ocfs2_validate_local_xe(inode, xe, block, block_end);
+ if (ret)
+ break;
+ }
*xe_index = i;
*found = 1;
ret = 0;
--
2.43.0
reply other threads:[~2026-04-29 2:20 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=20260429022005.1461600-1-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