From e36dec73dfdb054a2ecbf483ca52f1a9a900595d Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Mon, 25 May 2026 15:15:35 -0300 Subject: [PATCH] security: Round 3 complete - parsers boot/storage validation - A15: Multiboot2 parser validation (arch_early_setup.c): - Validate total_size range (8-65536 bytes) - Validate tag size (minimum 8 bytes) - Validate tag doesn't exceed buffer - Use cursor-based iteration with 8-byte alignment for next tag - F01: ext2 strict validation (ext2.c): - Validate rec_len >= 8 in all directory entry loops - Validate rec_len % 4 == 0 (4-byte alignment) - Validate rec_len doesn't exceed block boundary - Validate name_len < rec_len - 8 - Applied to: ext2_finddir, ext2_readdir_impl, ext2_dir_add_entry, ext2_dir_remove_entry, ext2_dir_find_entry, ext2_dir_is_empty Tests: 119/119 PASS (smoke test, SMP=4) --- src/arch/x86/arch_early_setup.c | 27 ++++++++++++++++++--- src/kernel/ext2.c | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/arch/x86/arch_early_setup.c b/src/arch/x86/arch_early_setup.c index c8d115e6..56b372d6 100644 --- a/src/arch/x86/arch_early_setup.c +++ b/src/arch/x86/arch_early_setup.c @@ -57,6 +57,11 @@ static uint32_t multiboot_copy_size; if (mbi_phys) { uint32_t total_size = *(volatile uint32_t*)mbi_phys; + /* A15: Validate total_size is reasonable */ + if (total_size < 8 || total_size > 65536) { + kprintf("[WARN] Invalid Multiboot2 total_size: %u\n", total_size); + total_size = 8; /* Use minimum valid size */ + } if (total_size >= 8) { multiboot_copy_size = total_size; if (multiboot_copy_size > sizeof(multiboot_copy)) { @@ -73,9 +78,22 @@ static uint32_t multiboot_copy_size; if (bi.arch_boot_info) { struct multiboot_tag* tag; - for (tag = (struct multiboot_tag*)((uint8_t*)bi.arch_boot_info + 8); - tag->type != MULTIBOOT_TAG_TYPE_END; - tag = (struct multiboot_tag*)((uint8_t*)tag + ((tag->size + 7) & ~7))) { + uint32_t cursor = 8; /* Skip header */ + while (cursor + 8 <= multiboot_copy_size) { + tag = (struct multiboot_tag*)((uint8_t*)bi.arch_boot_info + cursor); + + /* A15: Validate tag size */ + if (tag->size < 8) { + kprintf("[WARN] Invalid Multiboot2 tag size: %u\n", tag->size); + break; + } + if (cursor + tag->size > multiboot_copy_size) { + kprintf("[WARN] Multiboot2 tag exceeds buffer\n"); + break; + } + + if (tag->type == MULTIBOOT_TAG_TYPE_END) break; + if (tag->type == MULTIBOOT_TAG_TYPE_MODULE) { const struct multiboot_tag_module* mod = (const struct multiboot_tag_module*)tag; if (!bi.initrd_start) { @@ -96,6 +114,9 @@ static uint32_t multiboot_copy_size; bi.fb_bpp = fb->framebuffer_bpp; bi.fb_type = fb->framebuffer_type; } + + /* A15: Advance to next tag (8-byte aligned) */ + cursor += (tag->size + 7) & ~7; } } diff --git a/src/kernel/ext2.c b/src/kernel/ext2.c index 3371891b..644810ba 100644 --- a/src/kernel/ext2.c +++ b/src/kernel/ext2.c @@ -744,6 +744,13 @@ static fs_node_t* ext2_finddir(fs_node_t* node, const char* name) { struct ext2_dir_entry* de = (struct ext2_dir_entry*)(blk_buf + off); if (de->rec_len == 0) goto done; + /* F01: Validate rec_len */ + if (de->rec_len < 8) goto done; + if (de->rec_len % 4 != 0) goto done; + if (off + de->rec_len > bs) goto done; + /* F01: Validate name_len */ + if (de->name_len >= de->rec_len - 8) goto done; + if (de->inode != 0 && de->name_len == name_len) { if (memcmp(de->name, name, name_len) == 0) { struct ext2_inode child_inode; @@ -802,6 +809,13 @@ static int ext2_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* struct ext2_dir_entry* de = (struct ext2_dir_entry*)(blk_buf + off); if (de->rec_len == 0) goto done; + /* F01: Validate rec_len */ + if (de->rec_len < 8) goto done; + if (de->rec_len % 4 != 0) goto done; + if (off + de->rec_len > bs) goto done; + /* F01: Validate name_len */ + if (de->name_len >= de->rec_len - 8) goto done; + if (de->inode != 0) { /* Skip . and .. */ int skip = 0; @@ -865,6 +879,13 @@ static int ext2_dir_add_entry(struct ext2_mount* em, uint32_t dir_ino, const cha struct ext2_dir_entry* de = (struct ext2_dir_entry*)(blk_buf + off); if (de->rec_len == 0) break; + /* F01: Validate rec_len */ + if (de->rec_len < 8) break; + if (de->rec_len % 4 != 0) break; + if (off + de->rec_len > bs) break; + /* F01: Validate name_len */ + if (de->name_len >= de->rec_len - 8) break; + uint32_t actual_len = ((uint32_t)sizeof(struct ext2_dir_entry) + de->name_len + 3) & ~3U; uint32_t free_space = de->rec_len - actual_len; @@ -944,6 +965,13 @@ static int ext2_dir_remove_entry(struct ext2_mount* em, uint32_t dir_ino, const struct ext2_dir_entry* de = (struct ext2_dir_entry*)(blk_buf + off); if (de->rec_len == 0) break; + /* F01: Validate rec_len */ + if (de->rec_len < 8) break; + if (de->rec_len % 4 != 0) break; + if (off + de->rec_len > bs) break; + /* F01: Validate name_len */ + if (de->name_len >= de->rec_len - 8) break; + if (de->inode != 0 && de->name_len == name_len && memcmp(de->name, name, name_len) == 0) { if (removed_ino) *removed_ino = de->inode; @@ -991,6 +1019,13 @@ static int ext2_dir_find(struct ext2_mount* em, uint32_t dir_ino, const char* na struct ext2_dir_entry* de = (struct ext2_dir_entry*)(blk_buf + off); if (de->rec_len == 0) goto not_found; + /* F01: Validate rec_len */ + if (de->rec_len < 8) goto not_found; + if (de->rec_len % 4 != 0) goto not_found; + if (off + de->rec_len > bs) goto not_found; + /* F01: Validate name_len */ + if (de->name_len >= de->rec_len - 8) goto not_found; + if (de->inode != 0 && de->name_len == name_len && memcmp(de->name, name, name_len) == 0) { if (out_ino) *out_ino = de->inode; @@ -1028,6 +1063,13 @@ static int ext2_dir_is_empty(struct ext2_mount* em, uint32_t dir_ino) { struct ext2_dir_entry* de = (struct ext2_dir_entry*)(blk_buf + off); if (de->rec_len == 0) return 1; + /* F01: Validate rec_len */ + if (de->rec_len < 8) return 1; + if (de->rec_len % 4 != 0) return 1; + if (off + de->rec_len > bs) return 1; + /* F01: Validate name_len */ + if (de->name_len >= de->rec_len - 8) return 1; + if (de->inode != 0) { int is_dot = (de->name_len == 1 && de->name[0] == '.'); int is_dotdot = (de->name_len == 2 && de->name[0] == '.' && de->name[1] == '.'); -- 2.43.0