[PATCH] ocfs2: validate dx entry list counts on read
ZhengYuan Huang <[email protected]>
| Newsgroups | dev.linux.lists.ocfs2-devel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
[BUG] A corrupt indexed directory block can carry de_num_used larger than de_count. When create() needs to rebalance the directory index, that corrupt count reaches ocfs2_dx_dir_transfer_leaf() and trips fortify: kernel BUG at lib/string_helpers.c:1043! RIP: 0010:__fortify_panic+0x24/0x30 lib/string_helpers.c:1043 Call Trace: fortify_memset_chk include/linux/fortify-string.h:497 [inline] ocfs2_dx_dir_transfer_leaf fs/ocfs2/dir.c:3677 [inline] ocfs2_dx_dir_rebalance.isra.0+0x231a/0x24c0 fs/ocfs2/dir.c:3873 ocfs2_find_dir_space_dx.isra.0+0x32c/0x5f0 fs/ocfs2/dir.c:3950 ocfs2_prepare_dx_dir_for_insert.isra.0+0x750/0xf20 fs/ocfs2/dir.c:4207 ocfs2_prepare_dir_for_insert+0xe79/0x1630 fs/ocfs2/dir.c:4288 ocfs2_mknod+0x81c/0x2400 fs/ocfs2/namei.c:297 ocfs2_create+0x158/0x390 fs/ocfs2/namei.c:676 lookup_open.isra.0+0x10a1/0x1460 fs/namei.c:3796 open_last_lookups fs/namei.c:3895 [inline] path_openat+0x11fe/0x2ce0 fs/namei.c:4131 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_openat fs/open.c:1468 [inline] __se_sys_openat fs/open.c:1463 [inline] __x64_sys_openat+0x15b/0x220 fs/open.c:1463 ... [CAUSE] DX leaf and inline dx root blocks validate signatures and ECC, but they do not validate the geometry of struct ocfs2_dx_entry_list. Corrupt de_count/de_num_used values therefore survive block read and are later used as traversal bounds and memset lengths in lookup, delete and rebalance paths. [FIX] Validate dx entry-list geometry in the metadata read callbacks. de_count must match the block layout for the current superblock, and de_num_used must not exceed de_count. Do this for both dx leaves and inline dx roots so corrupted metadata is rejected before any caller walks or rewrites de_entries[]. Signed-off-by: ZhengYuan Huang <[email protected]> --- No Fixes tag is included because this is a missing metadata validation check, not a regression attributable to a single introducing commit. --- fs/ocfs2/dir.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 8e6b03238327..a89634e20b32 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -578,6 +578,29 @@ static int ocfs2_read_dir_block_direct(struct inode *dir, u64 phys, return ret; } +static int ocfs2_validate_dx_entry_list(struct super_block *sb, u64 blkno, + struct ocfs2_dx_entry_list *entry_list, + unsigned int expected, + const char *owner) +{ + unsigned int count = le16_to_cpu(entry_list->de_count); + unsigned int num_used = le16_to_cpu(entry_list->de_num_used); + + if (count != expected) + return ocfs2_error(sb, + "%s # %llu has invalid de_count %u (expected %u)\n", + owner, (unsigned long long)blkno, count, + expected); + + if (num_used > count) + return ocfs2_error(sb, + "%s # %llu has invalid de_num_used %u (de_count %u)\n", + owner, (unsigned long long)blkno, num_used, + count); + + return 0; +} + static int ocfs2_validate_dx_root(struct super_block *sb, struct buffer_head *bh) { @@ -604,7 +625,14 @@ static int ocfs2_validate_dx_root(struct super_block *sb, goto bail; } - if (!(dx_root->dr_flags & OCFS2_DX_FLAG_INLINE)) { + if (dx_root->dr_flags & OCFS2_DX_FLAG_INLINE) { + ret = ocfs2_validate_dx_entry_list(sb, bh->b_blocknr, + &dx_root->dr_entries, + ocfs2_dx_entries_per_root(sb), + "Dir Index Root"); + if (ret) + goto bail; + } else { struct ocfs2_extent_list *el = &dx_root->dr_list; if (le16_to_cpu(el->l_count) != ocfs2_extent_recs_per_dx_root(sb)) { @@ -660,13 +688,21 @@ static int ocfs2_validate_dx_leaf(struct super_block *sb, mlog(ML_ERROR, "Checksum failed for dir index leaf block %llu\n", (unsigned long long)bh->b_blocknr); - return ret; + goto bail; } if (!OCFS2_IS_VALID_DX_LEAF(dx_leaf)) { ret = ocfs2_error(sb, "Dir Index Leaf has bad signature %.*s\n", 7, dx_leaf->dl_signature); + goto bail; } + ret = ocfs2_validate_dx_entry_list(sb, bh->b_blocknr, + &dx_leaf->dl_list, + ocfs2_dx_entries_per_leaf(sb), + "Dir Index Leaf"); + +bail: + return ret; } -- 2.43.0