:: commit 40f84d0e77dd270086220a34f6b44468480dc9ca

Mintsuki <mintsuki@protonmail.com> — 2025-12-26 10:13

parents: 291dc84c1a

fs, lib/part: Use builtin overflow detection for arithmetic operations

diff --git a/common/fs/fat32.s2.c b/common/fs/fat32.s2.c
index 55981c1f..d18750cb 100644
--- a/common/fs/fat32.s2.c
+++ b/common/fs/fat32.s2.c
@@ -156,12 +156,26 @@ bytes_per_sector_valid:;
 
     // The following mess to identify the FAT type is from the FAT spec
     // at paragraph 3.5
-    size_t root_dir_sects = ((bpb.root_entries_count * 32) + (bpb.bytes_per_sector - 1)) / bpb.bytes_per_sector;
+    uint64_t root_dir_bytes;
+    if (__builtin_mul_overflow((uint64_t)bpb.root_entries_count, 32ULL, &root_dir_bytes)) {
+        return 1;
+    }
+    size_t root_dir_sects = (root_dir_bytes + (bpb.bytes_per_sector - 1)) / bpb.bytes_per_sector;
 
     // Calculate total sectors and metadata sectors separately to check for underflow
     uint64_t total_sects = bpb.sectors_count_16 ? bpb.sectors_count_16 : bpb.sectors_count_32;
     uint64_t sectors_per_fat = bpb.sectors_per_fat_16 ? bpb.sectors_per_fat_16 : bpb.sectors_per_fat_32;
-    uint64_t metadata_sects = (uint64_t)bpb.reserved_sectors + ((uint64_t)bpb.fats_count * sectors_per_fat) + root_dir_sects;
+    uint64_t fat_total_sects;
+    if (__builtin_mul_overflow((uint64_t)bpb.fats_count, sectors_per_fat, &fat_total_sects)) {
+        return 1;
+    }
+    uint64_t metadata_sects;
+    if (__builtin_add_overflow((uint64_t)bpb.reserved_sectors, fat_total_sects, &metadata_sects)) {
+        return 1;
+    }
+    if (__builtin_add_overflow(metadata_sects, root_dir_sects, &metadata_sects)) {
+        return 1;
+    }
 
     // Check for underflow before subtraction
     if (metadata_sects >= total_sects) {
@@ -193,7 +207,14 @@ bytes_per_sector_valid:;
     context->root_entries = bpb.root_entries_count;
 
     // Calculate root_start with overflow check
-    uint64_t root_start_64 = (uint64_t)context->reserved_sectors + (uint64_t)context->number_of_fats * context->sectors_per_fat;
+    uint64_t fat_sectors_64;
+    if (__builtin_mul_overflow((uint64_t)context->number_of_fats, (uint64_t)context->sectors_per_fat, &fat_sectors_64)) {
+        return 1;
+    }
+    uint64_t root_start_64;
+    if (__builtin_add_overflow((uint64_t)context->reserved_sectors, fat_sectors_64, &root_start_64)) {
+        return 1;
+    }
     if (root_start_64 > UINT32_MAX) {
         return 1;  // Overflow in root_start calculation
     }
@@ -203,10 +224,9 @@ bytes_per_sector_valid:;
         case 12:
         case 16:
             // Check for overflow in data_start_lba calculation
-            if (context->root_start > UINT32_MAX - context->root_size) {
-                return 1;  // Overflow in data_start_lba
+            if (__builtin_add_overflow(context->root_start, context->root_size, &context->data_start_lba)) {
+                return 1;
             }
-            context->data_start_lba = context->root_start + context->root_size;
             break;
         case 32:
             context->data_start_lba = context->root_start;
@@ -244,8 +264,14 @@ bytes_per_sector_valid:;
 }
 
 static int read_cluster_from_map(struct fat32_context *context, uint32_t cluster, uint32_t *out) {
-    uint64_t fat_base = (uint64_t)context->fat_start_lba * context->bytes_per_sector;
-    uint64_t fat_size = (uint64_t)context->sectors_per_fat * context->bytes_per_sector;
+    uint64_t fat_base;
+    if (__builtin_mul_overflow((uint64_t)context->fat_start_lba, (uint64_t)context->bytes_per_sector, &fat_base)) {
+        return -1;
+    }
+    uint64_t fat_size;
+    if (__builtin_mul_overflow((uint64_t)context->sectors_per_fat, (uint64_t)context->bytes_per_sector, &fat_size)) {
+        return -1;
+    }
 
     switch (context->type) {
         case 12: {
@@ -312,7 +338,11 @@ static uint32_t *cache_cluster_chain(struct fat32_context *context,
         return NULL;
     }
 
-    uint32_t *cluster_chain = ext_mem_alloc(chain_length * sizeof(uint32_t));
+    size_t alloc_size;
+    if (__builtin_mul_overflow(chain_length, sizeof(uint32_t), &alloc_size)) {
+        return NULL;
+    }
+    uint32_t *cluster_chain = ext_mem_alloc(alloc_size);
     cluster = initial_cluster;
     for (size_t i = 0; i < chain_length; i++) {
         cluster_chain[i] = cluster;
@@ -326,7 +356,10 @@ static bool read_cluster_chain(struct fat32_context *context,
                                uint32_t *cluster_chain,
                                size_t chain_len,
                                void *buf, uint64_t loc, uint64_t count) {
-    uint64_t block_size = (uint64_t)context->sectors_per_cluster * (uint64_t)context->bytes_per_sector;
+    uint64_t block_size;
+    if (__builtin_mul_overflow((uint64_t)context->sectors_per_cluster, (uint64_t)context->bytes_per_sector, &block_size)) {
+        return false;
+    }
     for (uint64_t progress = 0; progress < count;) {
         uint64_t block = (loc + progress) / block_size;
 
@@ -346,7 +379,19 @@ static bool read_cluster_chain(struct fat32_context *context,
         if (chunk > block_size - offset)
             chunk = block_size - offset;
 
-        uint64_t base = ((uint64_t)context->data_start_lba + (uint64_t)(cluster - 2) * context->sectors_per_cluster) * context->bytes_per_sector;
+        // Calculate base offset with overflow checks
+        uint64_t cluster_offset;
+        if (__builtin_mul_overflow((uint64_t)(cluster - 2), (uint64_t)context->sectors_per_cluster, &cluster_offset)) {
+            return false;
+        }
+        uint64_t sector_offset;
+        if (__builtin_add_overflow((uint64_t)context->data_start_lba, cluster_offset, &sector_offset)) {
+            return false;
+        }
+        uint64_t base;
+        if (__builtin_mul_overflow(sector_offset, (uint64_t)context->bytes_per_sector, &base)) {
+            return false;
+        }
         volume_read(context->part, buf + progress, base + offset, chunk);
 
         progress += chunk;
@@ -396,7 +441,10 @@ static bool fat32_filename_to_8_3(char *dest, const char *src) {
 }
 
 static int fat32_open_in(struct fat32_context* context, struct fat32_directory_entry* directory, struct fat32_directory_entry* file, const char* name) {
-    size_t block_size = context->sectors_per_cluster * context->bytes_per_sector;
+    size_t block_size;
+    if (__builtin_mul_overflow((size_t)context->sectors_per_cluster, (size_t)context->bytes_per_sector, &block_size)) {
+        return -1;
+    }
     char current_lfn[FAT32_LFN_MAX_FILENAME_LENGTH] = {0};
 
     size_t dir_chain_len;
@@ -413,17 +461,17 @@ static int fat32_open_in(struct fat32_context* context, struct fat32_directory_e
             return -1;
 
         // Check for integer overflow in allocation size
-        uint64_t alloc_size = (uint64_t)dir_chain_len * block_size;
-        if (alloc_size > SIZE_MAX || alloc_size > 256 * 1024 * 1024) {
+        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
             pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t));
             return -1;
         }
 
-        directory_entries = ext_mem_alloc((size_t)alloc_size);
+        directory_entries = ext_mem_alloc(alloc_size);
 
-        if (!read_cluster_chain(context, directory_cluster_chain, dir_chain_len, directory_entries, 0, (size_t)alloc_size)) {
-            pmm_free(directory_entries, (size_t)alloc_size);
+        if (!read_cluster_chain(context, directory_cluster_chain, dir_chain_len, directory_entries, 0, alloc_size)) {
+            pmm_free(directory_entries, alloc_size);
             pmm_free(directory_cluster_chain, dir_chain_len * sizeof(uint32_t));
             return -1;
         }
@@ -433,14 +481,19 @@ static int fat32_open_in(struct fat32_context* context, struct fat32_directory_e
         dir_chain_len = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), block_size);
 
         // Check for overflow
-        uint64_t alloc_size = (uint64_t)dir_chain_len * block_size;
-        if (alloc_size > SIZE_MAX || alloc_size > 256 * 1024 * 1024) {
+        size_t alloc_size;
+        if (__builtin_mul_overflow(dir_chain_len, block_size, &alloc_size) || alloc_size > 256 * 1024 * 1024) {
             return -1;
         }
 
-        directory_entries = ext_mem_alloc((size_t)alloc_size);
+        directory_entries = ext_mem_alloc(alloc_size);
 
-        volume_read(context->part, directory_entries, (uint64_t)context->root_start * context->bytes_per_sector, context->root_entries * sizeof(struct fat32_directory_entry));
+        uint64_t root_offset;
+        if (__builtin_mul_overflow((uint64_t)context->root_start, (uint64_t)context->bytes_per_sector, &root_offset)) {
+            pmm_free(directory_entries, alloc_size);
+            return -1;
+        }
+        volume_read(context->part, directory_entries, root_offset, context->root_entries * sizeof(struct fat32_directory_entry));
     }
 
     int ret;
diff --git a/common/fs/iso9660.s2.c b/common/fs/iso9660.s2.c
index 855e076d..35304840 100644
--- a/common/fs/iso9660.s2.c
+++ b/common/fs/iso9660.s2.c
@@ -106,7 +106,11 @@ static void iso9660_find_PVD(struct iso9660_primary_volume *desc, struct volume
     uint32_t max_lba = ISO9660_FIRST_VOLUME_DESCRIPTOR + ISO9660_MAX_VOLUME_DESCRIPTORS;
 
     while (lba < max_lba) {
-        volume_read(vol, desc, lba * ISO9660_SECTOR_SIZE, sizeof(struct iso9660_primary_volume));
+        uint64_t offset;
+        if (__builtin_mul_overflow((uint64_t)lba, (uint64_t)ISO9660_SECTOR_SIZE, &offset)) {
+            panic(false, "ISO9660: volume descriptor offset overflow");
+        }
+        volume_read(vol, desc, offset, sizeof(struct iso9660_primary_volume));
 
         switch (desc->volume_descriptor.type) {
         case ISO9660_VDT_PRIMARY:
@@ -136,7 +140,10 @@ static void iso9660_cache_root(struct volume *vol,
     }
 
     *root = ext_mem_alloc(*root_size);
-    uint64_t offset = (uint64_t)pv.root.extent.little * ISO9660_SECTOR_SIZE;
+    uint64_t offset;
+    if (__builtin_mul_overflow((uint64_t)pv.root.extent.little, (uint64_t)ISO9660_SECTOR_SIZE, &offset)) {
+        panic(false, "ISO9660: root extent offset overflow");
+    }
     volume_read(vol, *root, offset, *root_size);
 }
 
@@ -380,7 +387,12 @@ struct file_handle *iso9660_open(struct volume *vol, const char *path) {
 
         first = false;
 
-        uint64_t dir_offset = (uint64_t)next_sector * ISO9660_SECTOR_SIZE;
+        uint64_t dir_offset;
+        if (__builtin_mul_overflow((uint64_t)next_sector, (uint64_t)ISO9660_SECTOR_SIZE, &dir_offset)) {
+            pmm_free(current, current_size);
+            pmm_free(ret, sizeof(struct iso9660_file_handle));
+            return NULL;
+        }
         volume_read(vol, current, dir_offset, current_size);
     }
 
@@ -408,7 +420,14 @@ struct file_handle *iso9660_open(struct volume *vol, const char *path) {
 
 static void iso9660_read(struct file_handle *file, void *buf, uint64_t loc, uint64_t count) {
     struct iso9660_file_handle *f = file->fd;
-    uint64_t offset = (uint64_t)f->LBA * ISO9660_SECTOR_SIZE + loc;
+    uint64_t base_offset;
+    if (__builtin_mul_overflow((uint64_t)f->LBA, (uint64_t)ISO9660_SECTOR_SIZE, &base_offset)) {
+        panic(false, "iso9660: offset calculation overflow");
+    }
+    uint64_t offset;
+    if (__builtin_add_overflow(base_offset, loc, &offset)) {
+        panic(false, "iso9660: offset calculation overflow");
+    }
     volume_read(f->context->vol, buf, offset, count);
 }
 
diff --git a/common/lib/part.s2.c b/common/lib/part.s2.c
index 8314aa1a..79908160 100644
--- a/common/lib/part.s2.c
+++ b/common/lib/part.s2.c
@@ -74,12 +74,18 @@ bool volume_read(struct volume *volume, void *buffer, uint64_t loc, uint64_t cou
         panic(false, "Attempted volume_read() on pxe");
     }
 
-    uint64_t part_size = volume->sect_count * volume->sector_size;
+    uint64_t part_size;
+    if (__builtin_mul_overflow(volume->sect_count, volume->sector_size, &part_size)) {
+        return false;
+    }
     if (loc >= part_size || count > part_size - loc) {
         return false;
     }
 
-    uint64_t block_size = volume->fastest_xfer_size * volume->sector_size;
+    uint64_t block_size;
+    if (__builtin_mul_overflow(volume->fastest_xfer_size, volume->sector_size, &block_size)) {
+        return false;
+    }
 
     uint64_t progress = 0;
     while (progress < count) {
tab: 248 wrap: offon