DF-5478 / verdict.json
{ "finding_id": "DF-5478", "status": "reproduced", "reproduced": true, "impact": "dos", "confidence": "certain", "verdict": "The runtime-map guard at efirt.c:255-263 tests only the computed end pointer md_phys + md_pages*EFI_PAGE_SIZE; md_pages is never bounded directly, so firmware-fed md_pages = 2^52 (product wraps to 0) or 2^52+1 (product wraps to 4096) pass the guard while the fill loop at efirt.c:288-297 still runs md_pages (~4.5e15) iterations of kmalloc(M_EFI, M_WAITOK|ZERO) + vm_page_insert \u2014 an unkillable allocation engine at module-load time that exhausts memory and wedges the boot. Reproduced arithmetically on the audit guest with the verbatim guard expressions (df5478_wrap.c): both wrap inputs PASS the production guard with multi-petabyte loop bounds, while non-wrapping huge extents are correctly rejected. Fix validated in the same run: with the fix.diff md_pages cap, the wrap rows are rejected (loop never starts) and the sane 256-page case still passes. Trigger is a crafted EFI memory map (firmware/loader-fed at boot) \u2014 the unvalidated-table family; no runtime or unprivileged route.", "exploit_chain": "", "evidence": "run.log rows: wrap: md_pages = 2^52 product=0x0 end=0x0000000001000000 guard: PASS (BUG) -> fill-loop iterations=4503599627370496 (4.5e+15), kmalloc(M_WAITOK) bytes > 3.69e+08 GiB; wrap: md_pages = 2^52+1 ... guard: PASS (BUG); no-wrap 2^40 pages ... guard: REJECT (correct); FIXED-guard section: both wrap rows REJECT (loop never starts), sane case accept.", "kernel_refs": [ "sys/platform/pc64/x86_64/efirt.c:255", "sys/platform/pc64/x86_64/efirt.c:288", "sys/platform/pc64/x86_64/efirt.c:290", "sys/platform/pc64/x86_64/efirt.c:433", "sys/sys/efi.h:75" ], "poc_changes": "No seed existed. Model authored from scratch: verbatim guard arithmetic (uint64_t, EFI_PAGE_SIZE 4096, VM_MAX_USER_ADDRESS 0x00007fffffffe000) plus a FIXED-guard section for fix validation.", "attempts": 1, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 120, "guest_dirty": 0, "build_cmd": "cd /tmp/df5478 && cc -O2 -o df5478_wrap df5478_wrap.c", "run_cmd": "cd /tmp/df5478 && ./df5478_wrap", "code_hash": "49be001c9dccfe2b94ab1eddea7702a5014f433992bf7bf7b22824391f9c1346", "notes": "Rig limit: BIOS-boot guest cannot supply MODINFOMD_EFI_MAP metadata, so efi_create_1t1_map cannot run in-situ; the model reproduces the exact production integer arithmetic (nothing kernel-specific beyond M_WAITOK availability is involved).", "recommended_fix": "Reject md_pages >= VM_MAX_USER_ADDRESS / EFI_PAGE_SIZE before the end-pointer checks (fix.diff).", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (direct md_pages cap ahead of the existing wrap/bound checks) applied to the model: the previously-passing wrap inputs are now rejected and the sane control still passes. Kernel-rebuild validation not meaningful on this BIOS rig (path has no inputs here); arithmetic equivalence is the strongest validation this rig admits.", "fix_evidence": "run.log === under FIXED guard (fix.diff) === section: wrap: md_pages = 2^52 ... fixed guard: REJECT (loop never starts); sane 256 pages @16MB ... fixed guard: accept (loop runs)." } |