--- a/sys/platform/pc64/x86_64/efirt.c +++ b/sys/platform/pc64/x86_64/efirt.c @@ -466,6 +466,14 @@ lockuninit(&resettodr_lock); } +/* + * Sanity cap for the firmware-fed system-table config entry count. The + * EFI specification has no architected maximum, but real firmware tables + * carry a handful of entries; anything past this is a lie that would + * otherwise drive an unbounded walk over kernel memory. + */ +#define EFI_CFGTBL_MAX 256 + int efi_get_table(struct uuid *uuid, void **ptr) { @@ -474,10 +482,23 @@ if (efi_cfgtbl == NULL) return (ENXIO); + /* + * efi_cfgtbl holds the firmware-fed st_cfgtbl value, which is a + * PHYSICAL address: this kernel never calls SetVirtualAddressMap + * (see the 1:1 mapping design comment above efi_enter()), so table + * pointers stay physical for the lifetime of the system. They must + * be translated into the direct map before they can be dereferenced + * as kernel virtual addresses. + */ count = efi_systbl->st_entries; - ct = efi_cfgtbl; + if (count > EFI_CFGTBL_MAX) + return (EOVERFLOW); + ct = (struct efi_cfgtbl *)PHYS_TO_DMAP( + (vm_paddr_t)(uintptr_t)efi_systbl->st_cfgtbl); while (count--) { if (!bcmp(&ct->ct_uuid, uuid, sizeof(*uuid))) { + if (ct->ct_data >= DMAP_MAX_ADDRESS - DMAP_MIN_ADDRESS) + return (ERANGE); *ptr = (void *)PHYS_TO_DMAP(ct->ct_data); return (0); }