F-4714: compute GPT header offset in 64 bits to prevent uint32_t overflow

The protective-MBR lba_first field (attacker-controlled uint32_t, no range
check) was multiplied by GPT_SECTOR_SIZE (int 512) in disk_open. C's usual
arithmetic conversions made the product a 32-bit unsigned, which wraps for
gpt_lba >= 0x800000 (512 * 0x800001 -> 0x200), silently redirecting the GPT
header read back to LBA 1 instead of the out-of-range LBA named. Cast the
constant to uint64_t so the byte offset is computed in 64 bits. Add a unit
test that points lba_first at an overflowing LBA and confirms disk_open now
rejects it.
pull/792/head
Daniele Lacamera 2026-06-10 13:21:56 +02:00
parent e0f271bfd5
commit 93a8fe8d5a
2 changed files with 28 additions and 1 deletions

View File

@ -149,7 +149,8 @@ int disk_open(int drv)
wolfBoot_printf("Found GPT PTE at sector %u\r\n", gpt_lba);
/* Read GPT header */
r = disk_read(drv, GPT_SECTOR_SIZE * gpt_lba, GPT_SECTOR_SIZE, sector);
r = disk_read(drv, (uint64_t)GPT_SECTOR_SIZE * gpt_lba, GPT_SECTOR_SIZE,
sector);
if (r < 0) {
wolfBoot_printf("Disk read failed\r\n");
Drives[drv].is_open = 0;

View File

@ -653,6 +653,31 @@ START_TEST(test_disk_open_gpt_rejects_huge_part_array)
}
END_TEST
START_TEST(test_disk_open_gpt_lba_no_overflow)
{
/* The protective-MBR lba_first is an attacker-controlled uint32_t. The
* GPT-header byte offset must be computed in 64 bits. If it is computed
* as the 32-bit product GPT_SECTOR_SIZE * gpt_lba, it wraps for
* gpt_lba >= 0x800000: 512 * 0x800001 wraps to 0x200, silently
* redirecting the read back to LBA 1 (the real GPT header) instead of
* the out-of-range LBA the field actually names. */
struct gpt_mbr_part_entry *mbr_entry;
build_gpt_disk();
/* Point the protective entry at an LBA whose 512* product overflows a
* 32-bit unsigned back to 0x200 (LBA 1). */
mbr_entry = (struct gpt_mbr_part_entry *)(fake_disk + GPT_MBR_ENTRY_START);
mbr_entry->lba_first = 0x800001;
/* With correct 64-bit arithmetic the header read targets byte
* 0x100000200, far past the fake disk, so disk_open must fail rather
* than wrap to LBA 1 and accept the table. */
ck_assert_int_eq(disk_open(0), -1);
ck_assert_int_eq(Drives[0].is_open, 0);
}
END_TEST
START_TEST(test_disk_open_gpt_empty_entry_mid_table)
{
/* GPT header says 3 partitions but entry[1] has zeroed type GUID.
@ -990,6 +1015,7 @@ Suite *wolfboot_suite(void)
tcase_add_test(tc_cov, test_disk_open_gpt_excess_partitions);
tcase_add_test(tc_cov, test_disk_open_gpt_large_array_sz);
tcase_add_test(tc_cov, test_disk_open_gpt_rejects_huge_part_array);
tcase_add_test(tc_cov, test_disk_open_gpt_lba_no_overflow);
tcase_add_test(tc_cov, test_disk_open_gpt_empty_entry_mid_table);
tcase_add_test(tc_cov, test_disk_open_mbr_zero_lba_entry);
tcase_add_test(tc_cov, test_open_part_invalid_drive);