diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c index 6c79eedf..3fbf00a7 100644 --- a/e2fsck/pass1.c +++ b/e2fsck/pass1.c @@ -286,15 +286,17 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) struct ext2_super_block *sb = ctx->fs->super; struct ext2_inode_large *inode; struct ext2_ext_attr_entry *entry; - char *start; + char *start, *header; unsigned int storage_size, remain; problem_t problem = 0; + region_t region = 0; inode = (struct ext2_inode_large *) pctx->inode; storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize; - start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE + - inode->i_extra_isize + sizeof(__u32); + header = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize; + start = header + sizeof(__u32); entry = (struct ext2_ext_attr_entry *) start; /* scan all entry's headers first */ @@ -302,10 +304,28 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) /* take finish entry 0UL into account */ remain = storage_size - sizeof(__u32); + region = region_create(0, storage_size); + if (!region) { + fix_problem(ctx, PR_1_EA_ALLOC_REGION_ABORT, pctx); + problem = 0; + ctx->flags |= E2F_FLAG_ABORT; + return; + } + if (region_allocate(region, 0, sizeof(__u32))) { + problem = PR_1_INODE_EA_ALLOC_COLLISION; + goto fix; + } + while (remain >= sizeof(struct ext2_ext_attr_entry) && !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); @@ -333,6 +353,13 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) goto fix; } + if (entry->e_value_size && + region_allocate(region, sizeof(__u32) + entry->e_value_offs, + EXT2_EXT_ATTR_SIZE(entry->e_value_size))) { + problem = PR_1_INODE_EA_ALLOC_COLLISION; + goto fix; + } + hash = ext2fs_ext_attr_hash_entry(entry, start + entry->e_value_offs); @@ -347,7 +374,15 @@ static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx) entry = EXT2_EXT_ATTR_NEXT(entry); } + + if (region_allocate(region, (char *)entry - (char *)header, + sizeof(__u32))) { + problem = PR_1_INODE_EA_ALLOC_COLLISION; + goto fix; + } fix: + if (region) + region_free(region); /* * it seems like a corruption. it's very unlikely we could repair * EA(s) in automatic fashion -bzzz @@ -356,7 +391,7 @@ fix: return; /* simply remove all possible EA(s) */ - *((__u32 *)start) = 0UL; + *((__u32 *)header) = 0UL; e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode, EXT2_INODE_SIZE(sb), "pass1"); } diff --git a/e2fsck/problem.c b/e2fsck/problem.c index 2d29c35d..b982a278 100644 --- a/e2fsck/problem.c +++ b/e2fsck/problem.c @@ -977,6 +977,11 @@ static struct e2fsck_problem problem_table[] = { N_("@i %i passes checks, but checksum does not match @i. "), PROMPT_FIX, PR_PREEN_OK }, + /* Inode extended attribute is corrupt (allocation collision) */ + { PR_1_INODE_EA_ALLOC_COLLISION, + N_("@i %i @a is corrupt (allocation collision). "), + PROMPT_CLEAR, 0}, + /* * Inode extent block passes checks, but checksum does not match * extent diff --git a/e2fsck/problem.h b/e2fsck/problem.h index 89146ecd..f051c111 100644 --- a/e2fsck/problem.h +++ b/e2fsck/problem.h @@ -577,6 +577,9 @@ struct problem_context { /* inode passes checks, but checksum does not match inode */ #define PR_1_INODE_ONLY_CSUM_INVALID 0x010068 +/* Inode EA allocation collision */ +#define PR_1_INODE_EA_ALLOC_COLLISION 0x010069 + /* extent block passes checks, but checksum does not match extent block */ #define PR_1_EXTENT_ONLY_CSUM_INVALID 0x01006A diff --git a/tests/f_inode_ea_collision/expect.1 b/tests/f_inode_ea_collision/expect.1 new file mode 100644 index 00000000..a67a5f19 --- /dev/null +++ b/tests/f_inode_ea_collision/expect.1 @@ -0,0 +1,15 @@ +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 + +Inode 14 extended attribute is corrupt (allocation collision). Clear? yes + +Pass 2: Checking directory structure +Pass 3: Checking directory connectivity +Pass 4: Checking reference counts +Pass 5: Checking group summary information + +test_filesys: ***** FILE SYSTEM WAS MODIFIED ***** +test_filesys: 14/128 files (0.0% non-contiguous), 18/512 blocks +Exit status is 1 diff --git a/tests/f_inode_ea_collision/expect.2 b/tests/f_inode_ea_collision/expect.2 new file mode 100644 index 00000000..5a7ca86e --- /dev/null +++ b/tests/f_inode_ea_collision/expect.2 @@ -0,0 +1,7 @@ +Pass 1: Checking inodes, blocks, and sizes +Pass 2: Checking directory structure +Pass 3: Checking directory connectivity +Pass 4: Checking reference counts +Pass 5: Checking group summary information +test_filesys: 14/128 files (0.0% non-contiguous), 18/512 blocks +Exit status is 0 diff --git a/tests/f_inode_ea_collision/image.gz b/tests/f_inode_ea_collision/image.gz new file mode 100644 index 00000000..5217f650 Binary files /dev/null and b/tests/f_inode_ea_collision/image.gz differ diff --git a/tests/f_inode_ea_collision/name b/tests/f_inode_ea_collision/name new file mode 100644 index 00000000..b64119ec --- /dev/null +++ b/tests/f_inode_ea_collision/name @@ -0,0 +1 @@ +collisions in the inode ea area