]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
security: Round 3 complete - parsers boot/storage validation
authorTulio A M Mendes <[email protected]>
Mon, 25 May 2026 18:15:35 +0000 (15:15 -0300)
committerTulio A M Mendes <[email protected]>
Wed, 3 Jun 2026 04:02:35 +0000 (01:02 -0300)
- 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
src/kernel/ext2.c

index c8d115e6fdc913df6e9dbbae8b8f501d767b0410..56b372d61734d1cd1fc62c18e2f043f9c7c4b64d 100644 (file)
@@ -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;
         }
     }
 
index 3371891b7c98b1199cdd567820ada56ea26b3cf2..644810ba59bc13d45aa22db69f5888617b809f8f 100644 (file)
@@ -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] == '.');