* [PATCH 1/8] e2fsck: fix in-inode extended attribute checking
2026-09-24 22:23 [PATCH 0/8] e2fsprogs: fix extended attribute iteration loop bounds checking Eric Sandeen
@ 2026-09-24 22:23 ` Eric Sandeen
2026-09-24 22:23 ` [PATCH 2/8] e2fsck: fix extended attribute block checking Eric Sandeen
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Eric Sandeen @ 2026-09-24 22:23 UTC (permalink / raw)
To: linux-ext4; +Cc: tytso, sandeen, agruenba
From: Andreas Gruenbacher <agruenba@redhat.com>
Extended attribute areas in inodes and on separate blocks are stored in
the following format: (1) first comes the list of name records, (2)
followed by an end marker, followed by any (3) free space that remains,
followed by (4) any attribute values that are not stored in separate
inodes. The name records and values are all variable-length and 4-byte
aligned. The end marker is mandatory.
A lot of the loops iterating over those extended attribute areas get this
wrong. This patch fixes the loop in check_ea_in_inode:
- First, we can safely assume that the total size is at least 4 bytes.
Take 4 bytes (the size of the end marker) off of the remaining space
to make sure that enough space remains available for the end marker.
(We can safely assume that the total size is at least 4 bytes.)
- Second, at the top of the loop, we can assume that the area contains
at least an end marker, but we cannot assume that the list still
contains an entire ext2_ext_attr_entry header.
- Third, since the loop condition now no longer checks if we have a
complete ext2_ext_attr_entry header, check if we still have a complete
name record of size EXT2_EXT_ATTR_LEN(entry->e_name_len). The length
of the name is stored in the first byte of the entry, so we can safely
access it.
- Fourth, value are 4-byte aligned, so take that into consideration when
calculating the remaining free space.
With these changes to check_ea_in_inode(), an 'Extended attribute in
inode has Aa namelen which is invalid' error is detected before
allocation is checked, which masks the previous 'Inode extended attribute
is corrupt (allocation collision)' error in test f_inode_ea_collision.
Adjust the expected test result.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
e2fsck/pass1.c | 32 ++++++++++-------------------
tests/f_inode_ea_collision/expect.1 | 3 ++-
2 files changed, 13 insertions(+), 22 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index c9711446..555429b6 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -501,36 +501,31 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
goto fix;
}
- while (remain >= sizeof(struct ext2_ext_attr_entry) &&
- !EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
__u32 hash;
- if (region_allocate(region, (char *)entry - (char *)header,
- EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
- problem = PR_1_INODE_EA_ALLOC_COLLISION;
- goto fix;
- }
-
- /* header eats this space */
- remain -= sizeof(struct ext2_ext_attr_entry);
-
- /* is attribute name valid? */
- if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) {
+ /* entry->e_name_len is within the first four bytes */
+ if (EXT2_EXT_ATTR_LEN(entry->e_name_len) > remain) {
pctx->num = entry->e_name_len;
problem = PR_1_ATTR_NAME_LEN;
goto fix;
}
+ remain -= EXT2_EXT_ATTR_LEN(entry->e_name_len);
- /* attribute len eats this space */
- remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
+ if (region_allocate(region, (char *)entry - (char *)header,
+ EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
+ problem = PR_1_INODE_EA_ALLOC_COLLISION;
+ goto fix;
+ }
if (entry->e_value_inum == 0) {
/* check value size */
- if (entry->e_value_size > remain) {
+ if (EXT2_EXT_ATTR_SIZE(entry->e_value_size) > remain) {
pctx->num = entry->e_value_size;
problem = PR_1_ATTR_VALUE_SIZE;
goto fix;
}
+ remain -= EXT2_EXT_ATTR_SIZE(entry->e_value_size);
if (entry->e_value_size &&
region_allocate(region,
@@ -571,11 +566,6 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
ea_ibody_quota->inodes++;
}
- /* If EA value is stored in external inode then it does not
- * consume space here */
- if (entry->e_value_inum == 0)
- remain -= entry->e_value_size;
-
entry = EXT2_EXT_ATTR_NEXT(entry);
}
diff --git a/tests/f_inode_ea_collision/expect.1 b/tests/f_inode_ea_collision/expect.1
index a67a5f19..dfe49a9f 100644
--- a/tests/f_inode_ea_collision/expect.1
+++ b/tests/f_inode_ea_collision/expect.1
@@ -1,7 +1,8 @@
Pass 1: Checking inodes, blocks, and sizes
Inode 12 extended attribute is corrupt (allocation collision). Clear? yes
-Inode 13 extended attribute is corrupt (allocation collision). Clear? yes
+Extended attribute in inode 13 has a namelen (98) which is invalid
+Clear? yes
Inode 14 extended attribute is corrupt (allocation collision). Clear? yes
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 2/8] e2fsck: fix extended attribute block checking
2026-09-24 22:23 [PATCH 0/8] e2fsprogs: fix extended attribute iteration loop bounds checking Eric Sandeen
2026-09-24 22:23 ` [PATCH 1/8] e2fsck: fix in-inode extended attribute checking Eric Sandeen
@ 2026-09-24 22:23 ` Eric Sandeen
2026-09-24 22:23 ` [PATCH 3/8] e2fsck: fix ea loop in inc_ea_inode_refs Eric Sandeen
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Eric Sandeen @ 2026-09-24 22:23 UTC (permalink / raw)
To: linux-ext4; +Cc: tytso, sandeen, agruenba
From: Andreas Gruenbacher <agruenba@redhat.com>
Fix the same problems as in check_ea_in_inode().
The 'entry->e_value_offs + entry->e_value_size > fs->blocksize' check is
covered by the region_allocate() check that follows it, so it can be
removed.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
e2fsck/pass1.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 555429b6..f66a2908 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -2493,6 +2493,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
char * end;
struct ext2_ext_attr_header *header;
struct ext2_ext_attr_entry *first, *entry;
+ unsigned int remain;
blk64_t quota_blocks = EXT2FS_C2B(fs, 1);
__u64 quota_inodes = 0;
region_t region = 0;
@@ -2630,10 +2631,19 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
first = (struct ext2_ext_attr_entry *)(header+1);
end = block_buf + fs->blocksize;
+ /* take finish entry 0UL into account */
+ remain = end - (char *)first - sizeof(__u32);
entry = first;
- while ((char *)entry < end && *(__u32 *)entry) {
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
__u32 hash;
+ /* entry->e_name_len is within the first four bytes */
+ if (EXT2_EXT_ATTR_LEN(entry->e_name_len) > remain) {
+ if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
+ goto clear_extattr;
+ break;
+ }
+ remain -= EXT2_EXT_ATTR_LEN(entry->e_name_len);
if (region_allocate(region, (char *)entry - (char *)header,
EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
@@ -2649,13 +2659,12 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
break;
}
if (entry->e_value_inum == 0) {
- if (entry->e_value_size > EXT2_XATTR_SIZE_MAX ||
- (entry->e_value_offs + entry->e_value_size >
- fs->blocksize)) {
+ if (EXT2_EXT_ATTR_SIZE(entry->e_value_size) > remain) {
if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
goto clear_extattr;
break;
}
+ remain -= EXT2_EXT_ATTR_SIZE(entry->e_value_size);
if (entry->e_value_size &&
region_allocate(region, entry->e_value_offs,
EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
@@ -2697,7 +2706,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
entry = EXT2_EXT_ATTR_NEXT(entry);
}
- if (region_allocate(region, (char *)entry - (char *)header, 4)) {
+ if (region_allocate(region, (char *)entry - (char *)header, sizeof(__u32))) {
if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
goto clear_extattr;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 3/8] e2fsck: fix ea loop in inc_ea_inode_refs
2026-09-24 22:23 [PATCH 0/8] e2fsprogs: fix extended attribute iteration loop bounds checking Eric Sandeen
2026-09-24 22:23 ` [PATCH 1/8] e2fsck: fix in-inode extended attribute checking Eric Sandeen
2026-09-24 22:23 ` [PATCH 2/8] e2fsck: fix extended attribute block checking Eric Sandeen
@ 2026-09-24 22:23 ` Eric Sandeen
2026-09-24 22:23 ` [PATCH 4/8] libext2fs: fix ea loop in ext2fs_ext_attr_block_rehash Eric Sandeen
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Eric Sandeen @ 2026-09-24 22:23 UTC (permalink / raw)
To: linux-ext4; +Cc: tytso, sandeen, agruenba
From: Andreas Gruenbacher <agruenba@redhat.com>
Fix the same problems as in check_ea_in_inode().
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
e2fsck/pass1.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index f66a2908..e47e3b04 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -412,11 +412,16 @@ static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx,
struct ext2_ext_attr_entry *first, void *end)
{
struct ext2_ext_attr_entry *entry = first;
- struct ext2_ext_attr_entry *np = EXT2_EXT_ATTR_NEXT(entry);
+ unsigned int remain;
ea_value_t refs;
- while ((void *) entry < end && (void *) np < end &&
- !EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ /* take finish entry 0UL into account */
+ remain = end - (void *)first - sizeof(__u32);
+
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ if (EXT2_EXT_ATTR_LEN(entry->e_name_len) > remain)
+ break;
+ remain -= EXT2_EXT_ATTR_LEN(entry->e_name_len);
if (!entry->e_value_inum)
goto next;
if (!ctx->ea_inode_refs && !alloc_ea_inode_refs(ctx, pctx))
@@ -429,8 +434,7 @@ static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx,
refs += 1;
ea_refcount_store(ctx->ea_inode_refs, entry->e_value_inum, refs);
next:
- entry = np;
- np = EXT2_EXT_ATTR_NEXT(entry);
+ entry = EXT2_EXT_ATTR_NEXT(entry);
}
}
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 4/8] libext2fs: fix ea loop in ext2fs_ext_attr_block_rehash
2026-09-24 22:23 [PATCH 0/8] e2fsprogs: fix extended attribute iteration loop bounds checking Eric Sandeen
` (2 preceding siblings ...)
2026-09-24 22:23 ` [PATCH 3/8] e2fsck: fix ea loop in inc_ea_inode_refs Eric Sandeen
@ 2026-09-24 22:23 ` Eric Sandeen
2026-09-24 22:23 ` [PATCH 5/8] libext2fs: fix ea loop in read_xattrs_from_buffer Eric Sandeen
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Eric Sandeen @ 2026-09-24 22:23 UTC (permalink / raw)
To: linux-ext4; +Cc: tytso, sandeen, agruenba
From: Andreas Gruenbacher <agruenba@redhat.com>
Fix the same problems as in check_ea_in_inode().
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
lib/ext2fs/ext_attr.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 7723d0f9..914bff1d 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -163,10 +163,17 @@ void ext2fs_ext_attr_block_rehash(struct ext2_ext_attr_header *header,
struct ext2_ext_attr_entry *end)
{
struct ext2_ext_attr_entry *here;
+ unsigned int remain;
__u32 hash = 0;
here = (struct ext2_ext_attr_entry *)(header+1);
- while (here < end && !EXT2_EXT_IS_LAST_ENTRY(here)) {
+ /* take finish entry 0UL into account */
+ remain = (void *)end - (void *)here - sizeof(__u32);
+ while (!EXT2_EXT_IS_LAST_ENTRY(here)) {
+ /* entry->e_name_len is within the first four bytes */
+ if (EXT2_EXT_ATTR_LEN(here->e_name_len) > remain)
+ break;
+ remain -= EXT2_EXT_ATTR_LEN(here->e_name_len);
if (!here->e_hash) {
/* Block is not shared if an entry's hash value == 0 */
hash = 0;
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 5/8] libext2fs: fix ea loop in read_xattrs_from_buffer
2026-09-24 22:23 [PATCH 0/8] e2fsprogs: fix extended attribute iteration loop bounds checking Eric Sandeen
` (3 preceding siblings ...)
2026-09-24 22:23 ` [PATCH 4/8] libext2fs: fix ea loop in ext2fs_ext_attr_block_rehash Eric Sandeen
@ 2026-09-24 22:23 ` Eric Sandeen
2026-09-24 22:23 ` [PATCH 6/8] tune2fs: fix ea loop in update_xattr_entry_hashes Eric Sandeen
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Eric Sandeen @ 2026-09-24 22:23 UTC (permalink / raw)
To: linux-ext4; +Cc: tytso, sandeen, agruenba
From: Andreas Gruenbacher <agruenba@redhat.com>
Fix the same problems as in check_ea_in_inode().
There is no need for recalculating the remaining space in the second
iteration.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
lib/ext2fs/ext_attr.c | 38 ++++++++++++--------------------------
1 file changed, 12 insertions(+), 26 deletions(-)
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 914bff1d..6f6d2f4f 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -884,26 +884,23 @@ static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
/* find the end */
end = entries;
- remain = storage_size;
- while (remain >= sizeof(struct ext2_ext_attr_entry) &&
- !EXT2_EXT_IS_LAST_ENTRY(end)) {
-
- /* header eats this space */
- remain -= sizeof(struct ext2_ext_attr_entry);
-
- /* is attribute name valid? */
- if (EXT2_EXT_ATTR_SIZE(end->e_name_len) > remain)
+ /* take finish entry 0UL into account */
+ remain = storage_size - sizeof(__u32);
+ while (!EXT2_EXT_IS_LAST_ENTRY(end)) {
+ /* end->e_name_len is within the first four bytes */
+ if (EXT2_EXT_ATTR_LEN(end->e_name_len) > remain)
return EXT2_ET_EA_BAD_NAME_LEN;
-
- /* attribute len eats this space */
- remain -= EXT2_EXT_ATTR_SIZE(end->e_name_len);
+ remain -= EXT2_EXT_ATTR_LEN(end->e_name_len);
+ if (!end->e_value_inum) {
+ if (EXT2_EXT_ATTR_SIZE(end->e_value_size) > remain)
+ return EXT2_ET_EA_BAD_VALUE_SIZE;
+ remain -= EXT2_EXT_ATTR_SIZE(end->e_value_size);
+ }
end = EXT2_EXT_ATTR_NEXT(end);
}
entry = entries;
- remain = storage_size;
- while (remain >= sizeof(struct ext2_ext_attr_entry) &&
- !EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
/* Allocate space for more attrs? */
if (handle->count == handle->capacity) {
@@ -914,12 +911,6 @@ static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
x = handle->attrs + handle->count;
- /* header eats this space */
- remain -= sizeof(struct ext2_ext_attr_entry);
-
- /* attribute len eats this space */
- remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
-
/* Extract name */
prefix = find_ea_prefix(entry->e_name_index);
prefix_len = (prefix ? strlen(prefix) : 0);
@@ -942,9 +933,6 @@ static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
return EXT2_ET_BAD_EA_BLOCK_NUM;
if (entry->e_value_inum == 0) {
- if (entry->e_value_size > remain)
- return EXT2_ET_EA_BAD_VALUE_SIZE;
-
if (entry->e_value_offs + entry->e_value_size > values_size)
return EXT2_ET_EA_BAD_VALUE_OFFSET;
@@ -953,8 +941,6 @@ static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
(char *)end + sizeof(__u32))
return EXT2_ET_EA_BAD_VALUE_OFFSET;
- remain -= entry->e_value_size;
-
err = ext2fs_get_mem(entry->e_value_size, &x->value);
if (err)
return err;
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 6/8] tune2fs: fix ea loop in update_xattr_entry_hashes
2026-09-24 22:23 [PATCH 0/8] e2fsprogs: fix extended attribute iteration loop bounds checking Eric Sandeen
` (4 preceding siblings ...)
2026-09-24 22:23 ` [PATCH 5/8] libext2fs: fix ea loop in read_xattrs_from_buffer Eric Sandeen
@ 2026-09-24 22:23 ` Eric Sandeen
2026-09-24 22:23 ` [PATCH 7/8] resize2fs: " Eric Sandeen
2026-09-24 22:23 ` [PATCH 8/8] libext2fs: fix ea loop in ext2fs_xattr_inode_max_size Eric Sandeen
7 siblings, 0 replies; 9+ messages in thread
From: Eric Sandeen @ 2026-09-24 22:23 UTC (permalink / raw)
To: linux-ext4; +Cc: tytso, sandeen, agruenba
From: Andreas Gruenbacher <agruenba@redhat.com>
Fix the same problems as in check_ea_in_inode().
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
misc/tune2fs.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 2d85fb70..53a7a1b2 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -774,13 +774,19 @@ static int update_xattr_entry_hashes(ext2_filsys fs,
struct ext2_ext_attr_entry *entry,
struct ext2_ext_attr_entry *end)
{
+ unsigned int remain;
int modified = 0;
errcode_t retval;
- while (entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry)) {
- if ((char *) entry + sizeof(struct ext2_ext_attr_entry) >=
- (char *) end)
+ /* take finish entry 0UL into account */
+ remain = (void *)end - (void *)entry - sizeof(__u32);
+
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ /* end->e_name_len is within the first four bytes */
+ if (EXT2_EXT_ATTR_LEN(entry->e_name_len) > remain)
fatal_err(0, "corrupted extended attribute field");
+ remain -= EXT2_EXT_ATTR_LEN(entry->e_name_len);
+
if (entry->e_value_inum) {
retval = ext2fs_ext_attr_hash_entry2(fs, entry, NULL,
&entry->e_hash);
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 7/8] resize2fs: fix ea loop in update_xattr_entry_hashes
2026-09-24 22:23 [PATCH 0/8] e2fsprogs: fix extended attribute iteration loop bounds checking Eric Sandeen
` (5 preceding siblings ...)
2026-09-24 22:23 ` [PATCH 6/8] tune2fs: fix ea loop in update_xattr_entry_hashes Eric Sandeen
@ 2026-09-24 22:23 ` Eric Sandeen
2026-09-24 22:23 ` [PATCH 8/8] libext2fs: fix ea loop in ext2fs_xattr_inode_max_size Eric Sandeen
7 siblings, 0 replies; 9+ messages in thread
From: Eric Sandeen @ 2026-09-24 22:23 UTC (permalink / raw)
To: linux-ext4; +Cc: tytso, sandeen, agruenba
From: Andreas Gruenbacher <agruenba@redhat.com>
Fix the same problems as in check_ea_in_inode().
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
resize/resize2fs.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index c8964af5..49aa039c 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -2039,13 +2039,28 @@ static void quiet_com_err_proc(const char *whoami EXT2FS_ATTR((unused)),
{
}
+#define fatal_err(code, args...) \
+ do { \
+ com_err(__func__, code, args); \
+ exit(1); \
+ } while (0);
+
static int fix_ea_entries(ext2_extent imap, struct ext2_ext_attr_entry *entry,
struct ext2_ext_attr_entry *end, ext2_ino_t last_ino)
{
+ unsigned int remain;
int modified = 0;
ext2_ino_t new_ino;
- while (entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ /* take finish entry 0UL into account */
+ remain = (void *)end - (void *)entry - sizeof(__u32);
+
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ /* end->e_name_len is within the first four bytes */
+ if (EXT2_EXT_ATTR_LEN(entry->e_name_len) > remain)
+ fatal_err(0, "corrupted extended attribute field");
+ remain -= EXT2_EXT_ATTR_LEN(entry->e_name_len);
+
if (entry->e_value_inum > last_ino) {
new_ino = ext2fs_extent_translate(imap,
entry->e_value_inum);
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 8/8] libext2fs: fix ea loop in ext2fs_xattr_inode_max_size
2026-09-24 22:23 [PATCH 0/8] e2fsprogs: fix extended attribute iteration loop bounds checking Eric Sandeen
` (6 preceding siblings ...)
2026-09-24 22:23 ` [PATCH 7/8] resize2fs: " Eric Sandeen
@ 2026-09-24 22:23 ` Eric Sandeen
7 siblings, 0 replies; 9+ messages in thread
From: Eric Sandeen @ 2026-09-24 22:23 UTC (permalink / raw)
To: linux-ext4; +Cc: tytso, sandeen, agruenba
From: Andreas Gruenbacher <agruenba@redhat.com>
Fix the same problems as in check_ea_in_inode().
Found by AISLE in partnership with Red Hat.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
lib/ext2fs/ext_attr.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/lib/ext2fs/ext_attr.c b/lib/ext2fs/ext_attr.c
index 6f6d2f4f..f2ac8911 100644
--- a/lib/ext2fs/ext_attr.c
+++ b/lib/ext2fs/ext_attr.c
@@ -1211,8 +1211,8 @@ errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
struct ext2_ext_attr_entry *entry;
struct ext2_inode_large *inode;
__u32 ea_inode_magic;
- unsigned int minoff;
- char *start;
+ unsigned int minoff, remain;
+ char *start, *end;
size_t i;
errcode_t err;
@@ -1241,14 +1241,25 @@ errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
inode->i_extra_isize, sizeof(__u32));
if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
/* has xattrs. calculate the size */
- start= ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
inode->i_extra_isize + sizeof(__u32);
+ end = (char *)inode + EXT2_INODE_SIZE(fs->super);
+ /* take finish entry 0UL into account */
+ remain = end - (char *)start - sizeof(__u32);
entry = (struct ext2_ext_attr_entry *) start;
while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ /* end->e_name_len is within the first four bytes */
+ if (EXT2_EXT_ATTR_LEN(entry->e_name_len) > remain)
+ return EXT2_ET_INLINE_DATA_NO_SPACE;
+ remain -= EXT2_EXT_ATTR_LEN(entry->e_name_len);
+
if (!entry->e_value_inum && entry->e_value_size) {
unsigned int offs = entry->e_value_offs;
if (offs < minoff)
minoff = offs;
+ if (EXT2_EXT_ATTR_SIZE(entry->e_value_size) > remain)
+ return EXT2_ET_INLINE_DATA_NO_SPACE;
+ remain -= EXT2_EXT_ATTR_SIZE(entry->e_value_size);
}
entry = EXT2_EXT_ATTR_NEXT(entry);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread