:: commit 055e9a203afc988974ef9b8e919e365061470a6c

Mintsuki <mintsuki@protonmail.com> — 2025-12-24 21:18

parents: 90bc2e05e5

lib/part: Miscellaneous security and bug fixes

diff --git a/common/lib/part.s2.c b/common/lib/part.s2.c
index ad45d8d2..11ec69e0 100644
--- a/common/lib/part.s2.c
+++ b/common/lib/part.s2.c
@@ -34,9 +34,19 @@ 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;
+    }
+    if (first_sect > UINT64_MAX - block_offset) {
+        return false;
+    }
+    uint64_t read_sector = first_sect + block_offset;
+
     for (;;) {
         int ret = disk_read_sectors(volume, volume->cache,
-                           first_sect + block * volume->fastest_xfer_size,
+                           read_sector,
                            xfer_size);
 
         switch (ret) {
@@ -189,15 +199,58 @@ static int gpt_get_part(struct volume *ret, struct volume *volume, int partition
     if ((uint32_t)partition >= header.number_of_partition_entries)
         return END_OF_TABLE;
 
+    // Validate partition entry size (must be at least as large as our struct)
+    uint32_t entry_size = header.size_of_partition_entry;
+    if (entry_size < sizeof(struct gpt_entry)) {
+        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
+    }
+    // Use actual entry size from header for offset calculation
+    uint64_t partition_offset = (uint64_t)partition * entry_size;
+    if (entry_offset > UINT64_MAX - partition_offset) {
+        return INVALID_TABLE;  // Addition overflow would occur
+    }
+    entry_offset += partition_offset;
+
     struct gpt_entry entry = {0};
-    volume_read(volume, &entry,
-         (header.partition_entry_lba * lb_size) + (partition * sizeof(entry)),
-         sizeof(entry));
+    volume_read(volume, &entry, entry_offset, sizeof(entry));
 
     struct guid empty_guid = {0};
     if (!memcmp(&entry.unique_partition_guid, &empty_guid, sizeof(struct guid)))
         return NO_PARTITION;
 
+    // Validate that ending_lba >= starting_lba to prevent underflow
+    if (entry.ending_lba < entry.starting_lba) {
+        return NO_PARTITION;  // Invalid partition geometry
+    }
+
+    // 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
+    }
+
+    // Check for overflow in sect_count calculation
+    // First compute partition size in logical blocks
+    // Check if +1 would overflow (ending_lba == UINT64_MAX)
+    uint64_t partition_size = entry.ending_lba - entry.starting_lba;
+    if (partition_size == UINT64_MAX) {
+        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
+    }
+
 #if defined (UEFI)
     ret->efi_handle  = volume->efi_handle;
     ret->block_io    = volume->block_io;
@@ -209,8 +262,8 @@ static int gpt_get_part(struct volume *ret, struct volume *volume, int partition
     ret->is_optical  = volume->is_optical;
     ret->partition   = partition + 1;
     ret->sector_size = volume->sector_size;
-    ret->first_sect  = entry.starting_lba * (lb_size / 512);
-    ret->sect_count  = ((entry.ending_lba - entry.starting_lba) + 1) * (lb_size / 512);
+    ret->first_sect  = first_sect_result;
+    ret->sect_count  = sect_count_result;
     ret->backing_dev = volume;
 
     struct guid guid;
@@ -292,11 +345,20 @@ uint32_t mbr_get_id(struct volume *volume) {
     return ret;
 }
 
+// Maximum number of logical partitions to prevent infinite loops from circular EBR chains
+#define MAX_LOGICAL_PARTITIONS 256
+
 static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part,
                                 int partition) {
     struct mbr_entry entry;
 
+    // Limit partition index to prevent excessive iteration
+    if (partition >= MAX_LOGICAL_PARTITIONS) {
+        return END_OF_TABLE;
+    }
+
     uint64_t ebr_sector = 0;
+    uint64_t prev_ebr_sector = 0;
 
     for (int i = 0; i < partition; i++) {
         uint64_t entry_offset = ebr_sector * 512 + 0x1ce;
@@ -307,7 +369,19 @@ static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part
             return END_OF_TABLE;
         }
 
+        prev_ebr_sector = ebr_sector;
         ebr_sector = entry.first_sect;
+
+        // Detect circular chain: if new sector points to 0 or backwards, it's invalid
+        // (EBR sectors should always increase within the extended partition)
+        if (ebr_sector == 0 || (i > 0 && ebr_sector <= prev_ebr_sector)) {
+            return END_OF_TABLE;  // Circular or corrupted EBR chain
+        }
+
+        // Also check that ebr_sector is within the extended partition bounds
+        if (ebr_sector >= extended_part->sect_count) {
+            return END_OF_TABLE;  // EBR points outside extended partition
+        }
     }
 
     uint64_t entry_offset = ebr_sector * 512 + 0x1be;
@@ -317,6 +391,23 @@ static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part
     if (entry.type == 0)
         return NO_PARTITION;
 
+    // Validate sect_count is non-zero
+    if (entry.sect_count == 0) {
+        return NO_PARTITION;
+    }
+
+    // Check for overflow in first_sect calculation
+    uint64_t first_sect_64;
+    if (__builtin_add_overflow(extended_part->first_sect, ebr_sector, &first_sect_64)) {
+        return NO_PARTITION;  // Addition overflow
+    }
+    if (__builtin_add_overflow(first_sect_64, (uint64_t)entry.first_sect, &first_sect_64)) {
+        return NO_PARTITION;  // Addition overflow
+    }
+    if (first_sect_64 > UINT64_MAX - entry.sect_count) {
+        return NO_PARTITION;  // Partition would overflow
+    }
+
 #if defined (UEFI)
     ret->efi_handle  = extended_part->efi_handle;
     ret->block_io    = extended_part->block_io;
@@ -328,7 +419,7 @@ static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part
     ret->is_optical  = extended_part->is_optical;
     ret->partition   = partition + 4 + 1;
     ret->sector_size = extended_part->sector_size;
-    ret->first_sect  = extended_part->first_sect + ebr_sector + entry.first_sect;
+    ret->first_sect  = first_sect_64;
     ret->sect_count  = entry.sect_count;
     ret->backing_dev = extended_part->backing_dev;
 
@@ -369,6 +460,11 @@ static int mbr_get_part(struct volume *ret, struct volume *volume, int partition
             if (entry.type != 0x0f && entry.type != 0x05)
                 continue;
 
+            // Validate extended partition has non-zero size
+            if (entry.sect_count == 0) {
+                continue;
+            }
+
             struct volume extended_part = {0};
 
 #if defined (UEFI)
@@ -399,6 +495,16 @@ static int mbr_get_part(struct volume *ret, struct volume *volume, int partition
     if (entry.type == 0)
         return NO_PARTITION;
 
+    // Validate sect_count is non-zero
+    if (entry.sect_count == 0) {
+        return NO_PARTITION;
+    }
+
+    // Check for overflow: first_sect + sect_count must not overflow
+    if ((uint64_t)entry.first_sect > UINT64_MAX - entry.sect_count) {
+        return NO_PARTITION;  // Partition would overflow
+    }
+
 #if defined (UEFI)
     ret->efi_handle  = volume->efi_handle;
     ret->block_io    = volume->block_io;
@@ -438,6 +544,11 @@ static int mbr_get_part(struct volume *ret, struct volume *volume, int partition
 int part_get(struct volume *part, struct volume *volume, int partition) {
     int ret;
 
+    // Validate partition index is non-negative
+    if (partition < 0) {
+        return NO_PARTITION;
+    }
+
     ret = gpt_get_part(part, volume, partition);
     if (ret != INVALID_TABLE)
         return ret;
tab: 248 wrap: offon