[PATCH] jfs: reject malformed xattr entries in ea_get
From: Kyle Zeng
Date: Thu Jun 11 2026 - 17:30:34 EST
JFS checks that an extended attribute list's top-level size field
matches the inode EA descriptor before returning the list to callers.
That is not enough to prove that every entry in the list is contained
within that size.
__jfs_setxattr() walks existing entries and trusts EA_SIZE(ea). A
crafted filesystem can store an inline EA list whose aggregate size is
self-consistent, but whose first entry advertises a value length that
extends past END_EALIST(). Replacing that attribute then subtracts the
oversized old entry from xattr_size and appends the replacement at an
out-of-bounds address.
Validate each EA entry in ea_get() before any get, list, or set path can
consume it. Reject entries whose header is truncated, whose encoded
length crosses the end of the list, or whose encoded name lacks the
required trailing NUL byte.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Codex:gpt-5.5
Signed-off-by: Kyle Zeng <kylebot@xxxxxxxxxx>
---
fs/jfs/xattr.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c
index 11d7f74d207b..a7432cfabea2 100644
--- a/fs/jfs/xattr.c
+++ b/fs/jfs/xattr.c
@@ -118,6 +118,33 @@ static inline int copy_name(char *buffer, struct jfs_ea *ea)
/* Forward references */
static void ea_release(struct inode *inode, struct ea_buffer *ea_buf);
+static bool ea_entries_valid(struct jfs_ea_list *ealist, int size)
+{
+ char *p = (char *)FIRST_EA(ealist);
+ char *end = (char *)ealist + size;
+
+ if (size < sizeof(*ealist))
+ return false;
+
+ while (p < end) {
+ struct jfs_ea *ea = (struct jfs_ea *)p;
+ int ea_size;
+
+ if (p + sizeof(*ea) > end)
+ return false;
+
+ ea_size = EA_SIZE(ea);
+ if (p + ea_size > end)
+ return false;
+ if (ea->name[ea->namelen] != '\0')
+ return false;
+
+ p += ea_size;
+ }
+
+ return p == end;
+}
+
/*
* NAME: ea_write_inline
*
@@ -574,6 +601,15 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size)
goto clean_up;
}
+ if (!ea_entries_valid(ea_buf->xattr, ea_size)) {
+ pr_err("%s: invalid extended attribute entry\n", __func__);
+ print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 16, 1,
+ ea_buf->xattr, ea_size, 1);
+ ea_release(inode, ea_buf);
+ rc = -EIO;
+ goto clean_up;
+ }
+
return ea_size;
clean_up:
--
2.54.0