[PATCH v2 1/2] ocfs2: validate inline xattr metadata in ocfs2_xattr_ibody_get
Zhang Cen <[email protected]> Tue, 26 May 2026 11:40:39 +0800
| Newsgroups | dev.linux.lists.ocfs2-devel |
|---|---|
| Message-ID | <[email protected]> |
ocfs2_xattr_ibody_get() derives xs->header from
di->i_xattr_inline_size and then immediately hands that header to
ocfs2_xattr_find_entry(). If a corrupted inode advertises a zero,
too-small, or too-large inline xattr size, the get path can place the
header at the end of the inode block or outside it before reading
xh_count and walking entries.
Reuse the inline xattr size and xh_count validation that the list path
already relies on before parsing the in-inode header on the get path.
Reject corrupted inline metadata with -EFSCORRUPTED instead of walking
past the inline area.
A crafted image can otherwise trigger:
BUG: KASAN: use-after-free in ocfs2_xattr_find_entry+0x5a/0x170
Fixes: cf1d6c763fbc ("ocfs2: Add extended attribute support")
Assisted-by: Codex:gpt-5.5
Signed-off-by: Zhang Cen <[email protected]>
---
fs/ocfs2/xattr.c | 51 ++++++++++++++++++++++++++++++------------------
1 file changed, 32 insertions(+), 19 deletions(-)
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 86cfd4c2adf9..eabfaa02d8b8 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -967,24 +967,17 @@ int ocfs2_has_inline_xattr_value_outside(struct inode *inode,
return 0;
}
-static int ocfs2_xattr_ibody_list(struct inode *inode,
- struct ocfs2_dinode *di,
- char *buffer,
- size_t buffer_size)
+static int ocfs2_xattr_check_inline_xh(struct inode *inode,
+ struct ocfs2_dinode *di,
+ struct ocfs2_xattr_header **header)
{
- struct ocfs2_xattr_header *header = NULL;
- struct ocfs2_inode_info *oi = OCFS2_I(inode);
- int ret = 0;
- u16 xattr_count;
+ struct ocfs2_xattr_header *xh;
size_t max_entries;
u16 inline_size;
-
- if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
- return ret;
+ u16 xattr_count;
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,
@@ -994,12 +987,11 @@ static int ocfs2_xattr_ibody_list(struct inode *inode,
return -EFSCORRUPTED;
}
- header = (struct ocfs2_xattr_header *)
- ((void *)di + inode->i_sb->s_blocksize - inline_size);
-
- xattr_count = le16_to_cpu(header->xh_count);
+ xh = (struct ocfs2_xattr_header *)
+ ((void *)di + inode->i_sb->s_blocksize - inline_size);
+ xattr_count = le16_to_cpu(xh->xh_count);
max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) /
- sizeof(struct ocfs2_xattr_entry);
+ sizeof(struct ocfs2_xattr_entry);
if (xattr_count > max_entries) {
ocfs2_error(inode->i_sb,
@@ -1009,6 +1001,26 @@ static int ocfs2_xattr_ibody_list(struct inode *inode,
return -EFSCORRUPTED;
}
+ *header = xh;
+ return 0;
+}
+
+static int ocfs2_xattr_ibody_list(struct inode *inode,
+ struct ocfs2_dinode *di,
+ char *buffer,
+ size_t buffer_size)
+{
+ struct ocfs2_xattr_header *header = NULL;
+ struct ocfs2_inode_info *oi = OCFS2_I(inode);
+ int ret = 0;
+
+ if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL))
+ return ret;
+
+ ret = ocfs2_xattr_check_inline_xh(inode, di, &header);
+ if (ret)
+ return ret;
+
ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size);
return ret;
@@ -1200,8 +1212,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_check_inline_xh(inode, di, &xs->header);
+ if (ret)
+ return ret;
xs->base = (void *)xs->header;
xs->here = xs->header->xh_entries;
--
2.43.0