⬢ DragonFlyBSD Kernel Audit
DF-5478 / verdict.json
← back to finding ↓ download raw
{
  "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)."
}