:: commit 1b5b8e4620e39ceb59337583aaecebaab54a7868

Mintsuki <mintsuki@protonmail.com> — 2026-03-31 16:53

parents: 4d53b3bc70

misc: Add CHECKED_MUL macro, use it for all overflow-checked multiplications

diff --git a/common/fs/fat32.s2.c b/common/fs/fat32.s2.c
index cfd664e6..7875060e 100644
--- a/common/fs/fat32.s2.c
+++ b/common/fs/fat32.s2.c
@@ -442,10 +442,11 @@ static int fat32_open_in(struct fat32_context* context, struct fat32_directory_e
         if (directory_cluster_chain == NULL)
             return -1;
 
-        // Check for integer overflow in allocation size
-        size_t alloc_size;
-        if (__builtin_mul_overflow(dir_chain_len, block_size, &alloc_size) || alloc_size > 256 * 1024 * 1024) {
-            // Limit directory size to 256MB to prevent memory exhaustion
+        size_t alloc_size = CHECKED_MUL(dir_chain_len, block_size, ({
+            pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t));
+            return -1;
+        }));
+        if (alloc_size > 256 * 1024 * 1024) {
             pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t));
             return -1;
         }
@@ -462,9 +463,8 @@ static int fat32_open_in(struct fat32_context* context, struct fat32_directory_e
     } else {
         dir_chain_len = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), block_size, return 1);
 
-        // Check for overflow
-        size_t alloc_size;
-        if (__builtin_mul_overflow(dir_chain_len, block_size, &alloc_size) || alloc_size > 256 * 1024 * 1024) {
+        size_t alloc_size = CHECKED_MUL(dir_chain_len, block_size, return -1);
+        if (alloc_size > 256 * 1024 * 1024) {
             return -1;
         }
 
diff --git a/common/lib/acpi.c b/common/lib/acpi.c
index 52afaf13..53bb87c8 100644
--- a/common/lib/acpi.c
+++ b/common/lib/acpi.c
@@ -436,10 +436,7 @@ void efi_map_runtime_entries(void) {
         }
 
         uint64_t base = entry->PhysicalStart;
-        uint64_t length;
-        if (__builtin_mul_overflow(entry->NumberOfPages, (uint64_t)4096, &length)) {
-            continue;
-        }
+        uint64_t length = CHECKED_MUL(entry->NumberOfPages, (uint64_t)4096, continue);
 
         memmap_alloc_range(base, length, MEMMAP_RESERVED_MAPPED, 0, true, false, true);
     }
@@ -455,12 +452,12 @@ void efi_map_runtime_entries(void) {
     }
 
     if (gST->ConfigurationTable != NULL && gST->NumberOfTableEntries > 0) {
-        uint64_t ct_size;
-        if (!__builtin_mul_overflow(gST->NumberOfTableEntries,
-                (uint64_t)sizeof(EFI_CONFIGURATION_TABLE), &ct_size)
-         && ct_size <= UINT32_MAX) {
+        uint64_t ct_size = CHECKED_MUL(gST->NumberOfTableEntries,
+                (uint64_t)sizeof(EFI_CONFIGURATION_TABLE), goto skip_ct);
+        if (ct_size <= UINT32_MAX) {
             map_single_table((uintptr_t)gST->ConfigurationTable, (uint32_t)ct_size);
         }
+skip_ct:;
     }
 
     if (gST->FirmwareVendor != NULL) {
diff --git a/common/lib/elf.c b/common/lib/elf.c
index d7763c36..dc15ce3e 100644
--- a/common/lib/elf.c
+++ b/common/lib/elf.c
@@ -835,12 +835,11 @@ bool elf64_load(uint8_t *elf, size_t file_size, uint64_t *entry_point, uint64_t
         panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
     }
 
-    // Validate program header offset and size don't overflow
-    uint64_t phdr_table_end;
-    if (__builtin_mul_overflow((uint64_t)hdr->ph_num, (uint64_t)hdr->phdr_size, &phdr_table_end) ||
-        __builtin_add_overflow(phdr_table_end, hdr->phoff, &phdr_table_end)) {
-        panic(true, "elf: Program header table size overflow");
-    }
+    uint64_t phdr_table_end = CHECKED_ADD(
+        CHECKED_MUL((uint64_t)hdr->ph_num, (uint64_t)hdr->phdr_size,
+            panic(true, "elf: Program header table size overflow")),
+        hdr->phoff,
+        panic(true, "elf: Program header table size overflow"));
 
     if (phdr_table_end > file_size) {
         panic(true, "elf: Program header table extends beyond file bounds");
diff --git a/common/lib/misc.h b/common/lib/misc.h
index f0c59615..1e8286c5 100644
--- a/common/lib/misc.h
+++ b/common/lib/misc.h
@@ -81,6 +81,16 @@ uint64_t strtoui(const char *s, const char **end, int base);
     CHECKED_ADD_res; \
 })
 
+#define CHECKED_MUL(a, b, onerror) ({ \
+    __auto_type CHECKED_MUL_a = (a); \
+    __auto_type CHECKED_MUL_b = (b); \
+    typeof(CHECKED_MUL_a * CHECKED_MUL_b) CHECKED_MUL_res; \
+    if (__builtin_mul_overflow(CHECKED_MUL_a, CHECKED_MUL_b, &CHECKED_MUL_res)) { \
+        onerror; \
+    } \
+    CHECKED_MUL_res; \
+})
+
 #define DIV_ROUNDUP(a, b, onerror) ({ \
     __auto_type DIV_ROUNDUP_a = (a); \
     __auto_type DIV_ROUNDUP_b = (b); \
diff --git a/common/lib/misc.s2.c b/common/lib/misc.s2.c
index 5dbb29fe..d75d22cc 100644
--- a/common/lib/misc.s2.c
+++ b/common/lib/misc.s2.c
@@ -38,12 +38,11 @@ uint64_t strtoui(const char *s, const char **end, int base) {
                 *end = &s[i];
             break;
         }
-        uint64_t mul_result;
-        if (__builtin_mul_overflow(n, (uint64_t)base, &mul_result)) {
+        uint64_t mul_result = CHECKED_MUL(n, (uint64_t)base, ({
             if (end != NULL)
                 *end = &s[i];
             return UINT64_MAX;
-        }
+        }));
         n = CHECKED_ADD(mul_result, (uint64_t)d, ({
             if (end != NULL)
                 *end = &s[i];
diff --git a/common/lib/part.s2.c b/common/lib/part.s2.c
index 8d61c932..5225d91a 100644
--- a/common/lib/part.s2.c
+++ b/common/lib/part.s2.c
@@ -34,11 +34,7 @@ static bool cache_block(struct volume *volume, uint64_t block) {
 
     uint64_t xfer_size = volume->fastest_xfer_size;
 
-    // Check for overflow in sector offset calculation
-    uint64_t block_offset;
-    if (__builtin_mul_overflow(block, volume->fastest_xfer_size, &block_offset)) {
-        return false;
-    }
+    uint64_t block_offset = CHECKED_MUL(block, (uint64_t)volume->fastest_xfer_size, return false);
     uint64_t read_sector = CHECKED_ADD(first_sect, block_offset, return false);
 
     // Clamp xfer_size to remaining sectors in volume
@@ -75,10 +71,7 @@ bool volume_read(struct volume *volume, void *buffer, uint64_t loc, uint64_t cou
 
     if (volume->sect_count != (uint64_t)-1) {
         // sect_count is always in 512-byte sectors for both whole disks and partitions
-        uint64_t part_size;
-        if (__builtin_mul_overflow(volume->sect_count, (uint64_t)512, &part_size)) {
-            return false;
-        }
+        uint64_t part_size = CHECKED_MUL(volume->sect_count, (uint64_t)512, return false);
         if (loc >= part_size || count > part_size - loc) {
             return false;
         }
@@ -217,11 +210,7 @@ static int gpt_get_part(struct volume *ret, struct volume *volume, int partition
         return INVALID_TABLE;
     }
 
-    // Check for potential integer overflow in offset calculation
-    uint64_t entry_offset;
-    if (__builtin_mul_overflow((uint64_t)header.partition_entry_lba, (uint64_t)lb_size, &entry_offset)) {
-        return INVALID_TABLE;  // Multiplication overflow
-    }
+    uint64_t entry_offset = CHECKED_MUL((uint64_t)header.partition_entry_lba, (uint64_t)lb_size, return INVALID_TABLE);
     // Use actual entry size from header for offset calculation
     uint64_t partition_offset = (uint64_t)partition * entry_size;
     entry_offset = CHECKED_ADD(entry_offset, partition_offset, return INVALID_TABLE);
@@ -243,11 +232,7 @@ static int gpt_get_part(struct volume *ret, struct volume *volume, int partition
     // Calculate sector multiplier for lb_size conversion
     uint64_t sect_multiplier = lb_size / 512;
 
-    // Check for overflow in first_sect calculation
-    uint64_t first_sect_result;
-    if (__builtin_mul_overflow(entry.starting_lba, sect_multiplier, &first_sect_result)) {
-        return NO_PARTITION;  // Overflow in first_sect
-    }
+    uint64_t first_sect_result = CHECKED_MUL(entry.starting_lba, sect_multiplier, return NO_PARTITION);
 
     // Check for overflow in sect_count calculation
     // First compute partition size in logical blocks
@@ -257,10 +242,7 @@ static int gpt_get_part(struct volume *ret, struct volume *volume, int partition
         return NO_PARTITION;  // Partition size +1 would overflow
     }
     uint64_t partition_blocks = partition_size + 1;
-    uint64_t sect_count_result;
-    if (__builtin_mul_overflow(partition_blocks, sect_multiplier, &sect_count_result)) {
-        return NO_PARTITION;  // Overflow in sect_count
-    }
+    uint64_t sect_count_result = CHECKED_MUL(partition_blocks, sect_multiplier, return NO_PARTITION);
 
 #if defined (UEFI)
     ret->efi_handle  = volume->efi_handle;
diff --git a/common/mm/pmm.s2.c b/common/mm/pmm.s2.c
index 91c9c836..f4ab3771 100644
--- a/common/mm/pmm.s2.c
+++ b/common/mm/pmm.s2.c
@@ -383,10 +383,7 @@ void init_memmap(void) {
         }
 
         uint64_t base = entry->PhysicalStart;
-        uint64_t length;
-        if (__builtin_mul_overflow(entry->NumberOfPages, (uint64_t)4096, &length)) {
-            continue;
-        }
+        uint64_t length = CHECKED_MUL(entry->NumberOfPages, (uint64_t)4096, continue);
 
         memmap[memmap_entries].base = base;
         memmap[memmap_entries].length = length;
@@ -476,10 +473,7 @@ static void pmm_reclaim_uefi_mem(struct memmap_entry *m, size_t *_count, bool ra
             uint64_t base = r->base;
             uint64_t top = CHECKED_ADD(base, r->length, continue);
             uint64_t efi_base = entry->PhysicalStart;
-            uint64_t efi_size;
-            if (__builtin_mul_overflow(entry->NumberOfPages, (uint64_t)4096, &efi_size)) {
-                continue;  // Skip malformed entry
-            }
+            uint64_t efi_size = CHECKED_MUL(entry->NumberOfPages, (uint64_t)4096, continue);
 
             if (efi_base < base) {
                 if (efi_size <= base - efi_base)
@@ -618,11 +612,8 @@ void *ext_mem_alloc(uint64_t count) {
 }
 
 void *ext_mem_alloc_counted(uint64_t count, uint64_t elem_size) {
-    uint64_t total;
-    if (__builtin_mul_overflow(count, elem_size, &total)) {
-        panic(false, "ext_mem_alloc_counted: allocation size overflow");
-    }
-    return ext_mem_alloc(total);
+    return ext_mem_alloc(CHECKED_MUL(count, elem_size,
+        panic(false, "ext_mem_alloc_counted: allocation size overflow")));
 }
 
 void *ext_mem_alloc_type(uint64_t count, uint32_t type) {
diff --git a/common/protos/multiboot1.c b/common/protos/multiboot1.c
index 5beff944..6dff99f1 100644
--- a/common/protos/multiboot1.c
+++ b/common/protos/multiboot1.c
@@ -39,8 +39,8 @@ static size_t get_multiboot1_info_size(
     return ALIGN_UP(sizeof(struct multiboot1_info), 16, OVERFLOW) +
            ALIGN_UP(strlen(cmdline) + 1, 16, OVERFLOW) +
            ALIGN_UP(sizeof(LIMINE_BRAND), 16, OVERFLOW) +
-           ALIGN_UP(section_entry_size * section_num, 16, OVERFLOW) +
-           ALIGN_UP(sizeof(struct multiboot1_module) * modules_count, 16, OVERFLOW) +
+           ALIGN_UP(CHECKED_MUL(section_entry_size, section_num, OVERFLOW), 16, OVERFLOW) +
+           ALIGN_UP(CHECKED_MUL(sizeof(struct multiboot1_module), modules_count, OVERFLOW), 16, OVERFLOW) +
            ALIGN_UP(modules_cmdlines_size, 16, OVERFLOW) +
            ALIGN_UP(sizeof(struct multiboot1_mmap_entry) * MEMMAP_MAX, 16, OVERFLOW);
 #undef OVERFLOW
@@ -234,7 +234,8 @@ noreturn void multiboot1_load(char *config, char *cmdline) {
         mb1_info_alloc(&mb1_info_raw, sizeof(struct multiboot1_info));
 
     if (section_hdr_info_valid == true) {
-        size_t section_table_size = (size_t)section_hdr_info.section_entry_size * section_hdr_info.num;
+        size_t section_table_size = CHECKED_MUL(section_hdr_info.section_entry_size, section_hdr_info.num,
+            panic(true, "multiboot1: ELF section table size overflow"));
         if (section_hdr_info.section_offset > kernel_file_size ||
             section_table_size > kernel_file_size - section_hdr_info.section_offset) {
             panic(true, "multiboot1: ELF section headers out of bounds");
diff --git a/common/protos/multiboot2.c b/common/protos/multiboot2.c
index fe020fad..48746e3f 100644
--- a/common/protos/multiboot2.c
+++ b/common/protos/multiboot2.c
@@ -47,7 +47,7 @@ static size_t get_multiboot2_info_size(
         ALIGN_UP(sizeof(struct multiboot_tag_framebuffer), MULTIBOOT_TAG_ALIGN, OVERFLOW) +
         ALIGN_UP(sizeof(struct multiboot_tag_new_acpi) + sizeof(struct rsdp), MULTIBOOT_TAG_ALIGN, OVERFLOW) +
         ALIGN_UP(sizeof(struct multiboot_tag_old_acpi) + 20, MULTIBOOT_TAG_ALIGN, OVERFLOW) +
-        ALIGN_UP(sizeof(struct multiboot_tag_elf_sections) + section_entry_size * section_num, MULTIBOOT_TAG_ALIGN, OVERFLOW) +
+        ALIGN_UP(sizeof(struct multiboot_tag_elf_sections) + CHECKED_MUL(section_entry_size, section_num, OVERFLOW), MULTIBOOT_TAG_ALIGN, OVERFLOW) +
         ALIGN_UP(modules_size, MULTIBOOT_TAG_ALIGN, OVERFLOW) +
         ALIGN_UP(sizeof(struct multiboot_tag_load_base_addr), MULTIBOOT_TAG_ALIGN, OVERFLOW) +
         ALIGN_UP(smbios_tag_size, MULTIBOOT_TAG_ALIGN, OVERFLOW) +
@@ -505,7 +505,8 @@ reloc_fail:
             panic(true, "multiboot2: Cannot return ELF file information");
         }
     } else {
-        size_t section_table_size = (size_t)section_hdr_info.section_entry_size * section_hdr_info.num;
+        size_t section_table_size = CHECKED_MUL(section_hdr_info.section_entry_size, section_hdr_info.num,
+            panic(true, "multiboot2: ELF section table size overflow"));
         if (section_hdr_info.section_offset > kernel_file_size ||
             section_table_size > kernel_file_size - section_hdr_info.section_offset) {
             panic(true, "multiboot2: ELF section headers out of bounds");
tab: 248 wrap: offon