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, §_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");
