F-4711: bound e820 entries to prevent boot_params stack overflow

e820_add_entry_cb() appended every FSP-supplied resource descriptor into
boot_params->e820_table[] with no check against E820_MAX_ENTRIES_ZEROPAGE
(128). A HOB list with more than 128 EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
entries therefore wrote FSP-controlled addr/size/type triples past the
fixed-size table into the stack-allocated boot_params in load_linux(),
corrupting adjacent fields and the saved return address.

Reject any entry once the table is full (return non-zero, which aborts the
HOB iteration). e820_entries stays uint8_t since it is a fixed-offset field
in the Linux zero-page layout and the cap makes the 256 wrap unreachable.

Add unit-linux-loader-e820 regression test (x86 32bit, standalone) that
feeds 200 descriptors and asserts the table never overflows.
pull/788/head
Daniele Lacamera 2026-06-05 19:19:05 +02:00
parent 277e8013cc
commit a8a9eec96b
3 changed files with 92 additions and 1 deletions

View File

@ -73,8 +73,12 @@ static int e820_add_entry_cb(uint64_t start, uint64_t length, uint32_t type,
void *ctx)
{
struct boot_params *bp = (struct boot_params*)ctx;
struct boot_e820_entry *map = bp->e820_table + bp->e820_entries;
struct boot_e820_entry *map;
if (bp->e820_entries >= E820_MAX_ENTRIES_ZEROPAGE)
return -1;
map = bp->e820_table + bp->e820_entries;
map->addr = start;
map->size = length;
map->type = (type == EFI_RESOURCE_SYSTEM_MEMORY) ? E820_TYPE_RAM :

View File

@ -60,6 +60,7 @@ TESTS:=unit-parser unit-fdt unit-extflash unit-string unit-spi-flash unit-aes128
TESTS+=unit-tpm-check-rot-auth
TESTS+=unit-tpm-api-names
TESTS+=unit-fit-gzip unit-fit-nogzip
TESTS+=unit-linux-loader-e820
include unit-sign-encrypted-output.mkfrag
@ -264,6 +265,13 @@ unit-chacha20: ../../include/target.h unit-extflash.c
unit-pci: unit-pci.c ../../src/pci.c
gcc -o $@ $< $(CFLAGS) -DWOLFBOOT_USE_PCI $(LDFLAGS)
# linux_loader.c is x86 32bit only and pulls in inline asm guarded on 32bit;
# build standalone with -m32 and without coverage (no 32bit gcov/check libs).
unit-linux-loader-e820: ../../include/target.h unit-linux-loader-e820.c
gcc -m32 -o $@ unit-linux-loader-e820.c -I. -I../../src -I../../include \
-g -DUNIT_TEST -DWOLFBOOT_FSP -DUCODE0_ADDRESS=0 \
-DWOLFBOOT_LOAD_BASE=0x100000
unit-boot-x86-fsp: ../../include/target.h unit-boot-x86_fsp.c
gcc -o $@ $^ $(CFLAGS) -DWOLFBOOT_LOAD_BASE=0x100000 -DWOLFBOOT_FSP \
-DUCODE0_ADDRESS=0 -ffunction-sections -fdata-sections $(LDFLAGS) \

View File

@ -0,0 +1,79 @@
/* unit-linux-loader-e820.c
*
* Regression test for F-4711: e820_add_entry_cb() must not write past the
* fixed-size boot_params->e820_table[E820_MAX_ENTRIES_ZEROPAGE] when the FSP
* HOB list supplies more resource descriptors than the table can hold.
*
* Built for x86 32bit (the only target supported by linux_loader.c), without
* the check framework, since 32bit libcheck is not generally available.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "x86/hob.h"
#include "x86/linux_loader.h"
#include "../../src/x86/hob.c"
#include "../../src/x86/linux_loader.c"
/* More descriptors than the table can hold, to force the overflow path. */
#define N_ENTRIES 200
#define CANARY_LEN 2048
#define CANARY_BYTE 0xAA
/* boot_params followed by a canary region: any write past e820_table runs
* through the rest of boot_params and into the canary. */
static struct {
struct boot_params bp;
uint8_t canary[CANARY_LEN];
} obj;
static uint8_t hoblist[(N_ENTRIES + 1) *
sizeof(struct efi_hob_resource_descriptor)];
int main(void)
{
int i;
uint8_t *p = hoblist;
struct efi_hob_resource_descriptor *rd;
struct efi_hob_generic_header *end;
for (i = 0; i < N_ENTRIES; i++) {
rd = (struct efi_hob_resource_descriptor *)p;
memset(rd, 0, sizeof(*rd));
rd->header.hob_type = EFI_HOB_TYPE_RESOURCE_DESCRIPTOR;
rd->header.hob_length = sizeof(struct efi_hob_resource_descriptor);
rd->resource_type = EFI_RESOURCE_SYSTEM_MEMORY;
rd->physical_start = 0x4141414141414141ULL;
rd->resource_length = 0x4242424242424242ULL;
p += sizeof(struct efi_hob_resource_descriptor);
}
end = (struct efi_hob_generic_header *)p;
end->hob_type = EFI_HOB_TYPE_END_OF_HOB_LIST;
end->hob_length = sizeof(struct efi_hob_generic_header);
memset(&obj, 0, sizeof(obj));
memset(obj.canary, CANARY_BYTE, CANARY_LEN);
(void)memory_map_from_hoblist(&obj.bp, (struct efi_hob *)hoblist);
printf("e820_entries=%u\n", obj.bp.e820_entries);
for (i = 0; i < CANARY_LEN; i++) {
if (obj.canary[i] != CANARY_BYTE) {
printf("FAIL: e820_table overflow corrupted memory at +%d\n", i);
return 1;
}
}
if (obj.bp.e820_entries > E820_MAX_ENTRIES_ZEROPAGE) {
printf("FAIL: e820_entries=%u exceeds max %d\n",
obj.bp.e820_entries, E820_MAX_ENTRIES_ZEROPAGE);
return 1;
}
printf("PASS\n");
return 0;
}