EFI system-table / runtime-table ingestion is signature-only: th_crc32 never verified, efi_systbl_phys not range-checked before PHYS_TO_DMAP, st_rt never validated against the RT-mapped regions (firmware-fed ring-0 call targets), memory-map blob ingested without size cross-checks
Summary
The systab is validated by its 8-byte signature only (th_crc32 never computed); st_rt only NULL-checked with rt_hdr unverified and the service pointers never checked to lie inside the mapped RT regions - crafted systab/loader chain = arbitrary-PA ring-0 call target (the DF-5365/5432 primitive shape); the memory-map blob ingested without memory_size cross-checks. The unvalidated-table family precedent. Not verifiable on this BIOS rig. CRC+range+region-crosscheck fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5480 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.2 KB | β raw | |
| VERDICT.md | β | 3.5 KB | β raw | |
| verdict.json | β | 2.9 KB | view raw | |
| manifest.json | β | 694 B | view raw |
DF-5480 β EFI system-table / runtime-services-table ingestion is signature-only: no crc32 verification, no DMAP-range check on efi_systbl_phys, no validation that st_rt / service pointers lie inside the RT-mapped regions β firmware/loader-fed ring-0 call targets and boot-time wild pointers.
FILE: sys/platform/pc64/x86_64/efirt.c (pass 2)
ROOT CAUSE (path:line): * efirt.c:399-410: efi_systbl_phys (from loader metadata MODINFOMD_FW_HANDLE, machdep.c:2674; loader side stand/boot/efi/loader/bootinfo.c:397) is PHYS_TO_DMAP'd and validated ONLY by the 8-byte signature th_sig. The table checksum (struct efi_tblhdr.th_crc32, sys/efi.h:115) is never computed or compared β contrast the EFI spec, which defines the CRC32 over the whole table as the integrity check. No check that efi_systbl_phys is within the DMAP-backed physical range before the deref. * efirt.c:440-447: efi_runtime = (struct efi_rt *)efi_systbl->st_rt is only NULL-checked. The rt table header (rt_hdr.th_sig, th_crc32) is never validated, and the individual service pointers dispatched at efirt.c:499, 528, 543, 572, 587, 603 are called with no check that they fall inside the RT regions that efi_create_1t1_map() actually mapped. Inside the 1:1 window these are ring-0 call targets β a crafted systab/loader chain yields an arbitrary physical-address call at CPL0. * efirt.c:428-434: the memory-map blob (MODINFOMD_EFI_MAP) is ingested with only descriptor_size != 0 checked; memory_size is not cross-checked against the blob's real size, and memory_size % descriptor_size residue is ignored, so ndesc can exceed the entries actually present (OOB walk of boot metadata β usually rejected by the per-descriptor sanity checks, but not guaranteed; and see DF-5478 for what passes the checks).
THREAT: boot-time, firmware/loader-fed (unvalidated-table family, cf. DF-5430-5433 / DF-5339-5343). Runtime reachability: none β the tables are fixed at boot; only the root-gated services consume them. Severity Low per family precedent. Not verifiable on this BIOS-boot rig (efi_systbl never assigned); status inconclusive by rig limit.
PACK: analysis-only (README.md, VERDICT.md, verdict.json).
DF-5480 VERDICT β signature-only ingestion of the EFI systab / RT table
Status: inconclusive (rig limit: BIOS-boot guest; efi_systbl never assigned, so none of these paths can be driven here). The validation gaps themselves are source-certain; impact requires a crafted firmware/loader chain (unvalidated-table family, boot-time).
The gaps, precisely
- Checksum never verified.
struct efi_tblhdr(sys/efi.h:111-117) carriesth_crc32, defined by the EFI specification as the CRC32 over the entire table β the mechanism exactly meant to catch corruption or tampering.efi_init()checks onlyst_hdr.th_sig != EFI_SYSTBL_SIG(efirt.c:405). A grep for crc in efirt.c returns nothing. Same for the runtime-services table:efi_runtime = (struct efi_rt *)efi_systbl->st_rt(efirt.c:440-441) withrt_hdr.th_sig/rt_hdr.th_crc32never checked (sys/efi.h:122). - No range check on the table pointer.
efi_systbl = (struct efi_systbl *)PHYS_TO_DMAP(efi_systbl_phys)(efirt.c:404) withefi_systbl_phystaken verbatim from loader metadata (machdep.c:2674, MODINFOMD_FW_HANDLE; loader: bootinfo.c:397 passes the firmware ST pointer). PHYS_TO_DMAP of a value beyond the DMAP extent (DMAP_MAX_ADDRESS) produces a non-canonical/unmapped VA β boot-time kernel fault on the very first field read (the signature check at efirt.c:405). - Unvalidated ring-0 call targets. The service pointers dispatched
at efirt.c:499 (
rt_gettime), 528 (rt_reset), 543 (rt_settime), 572 (rt_getvar), 587 (rt_scanvar), 603 (rt_setvar) are called inside the 1:1 window with no check that each pointer lies within an EFI_MD_ATTR_RT region that efi_create_1t1_map() actually mapped (the map contents and st_rt come from the same untrusted systab/blob, so one does not constrain the other). A crafted chain yields an arbitrary physical-address instruction fetch at CPL0 β the same primitive shape as DF-5365 (lapic_map) / DF-5432 (ioapic_map), gated behind the boot chain. - Memory-map blob ingestion. efirt.c:428-434: only
descriptor_size == 0is rejected;memory_sizeis never validated against the blob's actual size nor checked formemory_size % descriptor_sizeresidue β ndesc = memory_size/descriptor_size can overrun the metadata blob (OOB read of preload data; usually rejected downstream by the per-descriptor checks at efirt.c:242-263, but nothing enforces it).
Why not verified here
BIOS-boot guest: efi_systbl_phys == 0 β efi_init returns ENXIO at
efirt.c:399-403 before any of these paths execute. The gaps are provable
from source alone (absence of validation is not runtime-dependent);
behavioral verification needs an EFI (OVMF) boot with crafted tables.
Fix direction
Validate efi_systbl_phys against the DMAP extent; verify st_hdr CRC32
(and rt_hdr signature+CRC when consuming st_rt); cross-check each service
pointer against the RT regions collected from the memory map; validate
memory_size/descriptor_size against the blob length before computing
ndesc.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testableNo fix validation possible on this rig (baseline not reachable).
Confirmed kernel references
- sys/platform/pc64/x86_64/efirt.c:399
- sys/platform/pc64/x86_64/efirt.c:404
- sys/platform/pc64/x86_64/efirt.c:405
- sys/platform/pc64/x86_64/efirt.c:428
- sys/platform/pc64/x86_64/efirt.c:433
- sys/platform/pc64/x86_64/efirt.c:440
- sys/platform/pc64/x86_64/efirt.c:499
- sys/platform/pc64/x86_64/machdep.c:2674
- sys/sys/efi.h:115
Detail
Evidence (decisive lines)
VERDICT.md gap-by-gap trace: no crc anywhere in efirt.c (grep), efirt.c:404-405 signature-only check, efirt.c:440-447 st_rt handling, efirt.c:428-434 blob ingestion, dispatch sites efirt.c:499/528/543/572/587/603.
PoC changes
No PoC: requires EFI boot with crafted firmware/loader tables; BIOS-boot rig cannot populate any of the inputs.
Verified recommended fix
Verify st_hdr/rt_hdr CRC32, range-check efi_systbl_phys against DMAP_MAX, cross-check service pointers against mapped RT regions, validate memory_size/descriptor_size against the blob length.
Verdict
EFI table ingestion is signature-only. efirt.c:399-410 validates the system table by its 8-byte signature alone β th_crc32 (sys/efi.h:115) is never computed or compared; efi_systbl_phys (loader-fed MODINFOMD_FW_HANDLE, machdep.c:2674, bootinfo.c:397) is PHYS_TO_DMAP'd at efirt.c:404 with no check against the DMAP extent. efirt.c:440-447 takes st_rt with only a NULL check β rt_hdr signature/CRC never verified, and the service pointers dispatched at efirt.c:499/528/543/572/587/603 are never checked to lie inside the RT regions efi_create_1t1_map() actually mapped, making them firmware/loader-fed ring-0 call targets inside the 1:1 window (same primitive shape as DF-5365/DF-5432). efirt.c:428-434 ingests the memory-map blob without cross-checking memory_size against the blob's real size or descriptor_size residue (ndesc can overrun the blob). All gaps are source-certain (absence of validation); behavior needs an EFI boot with crafted tables β impossible on this BIOS-boot rig (efi_init returns ENXIO at efirt.c:399-403 before any of it runs), hence inconclusive by rig limit. Boot-time, firmware/loader-fed: unvalidated-table family, Low severity per precedent.
No comments yet.