⬢ DragonFlyBSD Kernel Audit
DF-5466 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5466",
  "status": "reproduced",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "REPRODUCED on a production-behaviour kernel via the pure boot-config route: hw.icu.msi_start=\"-32\" in /boot/loader.conf (kenn confirmed -32; icu_abi_initmap() runs unconditionally at machdep.c:2785) poisons icu_abi_msi_start to -32 because kgetenv_int/strtoq accepts the sign and the &~0x1f mask never range-checks (icu_abi.c:323-324). The first ICU MSI allocation then scans from i=-32: on cpu1 the window icu_irqmaps[1][-32..-1] aliases cpu0s row tail [160..191] (all UNUSED) so it succeeds \u2014 msi_alloc returns rc=0 with NEGATIVE vectors (zone[0]=-32 zone[31]=-1), performs OOB map writes (icu_abi.c:495-496) and calls msi_setup(-32..-1, 1) (icu_abi.c:499), whose msi_intr[-32..-1] OOB reads (msi.c:433, rodata garbage) feed setidt(0..31, ..., cpu1) \u2014 overwriting cpu1s exception gates 0..31. The rigs int3 on cpu1 then produced a TRIPLE FAULT: the serial console shows triggering int3 immediately followed by the bootloader menu with zero trap text (#BP->smashed gate->fetch #PF->smashed gate->#DF->smashed gate->cold reset). No INVARIANTS KASSERT exists on this path \u2014 every kernel config is equally affected. Fix (clamp msi_start into [0, IDT_HWI_VECTORS)) validated on a rebuilt kernel with the SAME boot knob still set: allocation lands legally at vectors 32..63 (zone[0]=32), no OOB, no smash, guest healthy.",
  "exploit_chain": "root sets hw.icu.msi_start=-32 in loader.conf (or kenv) -> boot-time icu_abi_initmap() computes -32 (icu_abi.c:323-324) -> any ICU MSI alloc (on ICU-ABI machines: first PCI MSI probe at boot via nexus.c:595; here: KLD trigger) -> icu_abi.c:473 loop with negative i -> OOB read/write at icu_irqmaps[cpuid][-32..-1] (previous cpus row) + intrs[j]=-32+j + msi_setup(-32+j) -> msi_intr[-k] OOB read (rodata) + setidt(0..31, garbage, cpu) = arbitrary-cpu exception-gate overwrite -> any exception on that cpu redirects RIP to OOB-read kernel data -> triple fault / RIP-controlled crash. On a real ICU-ABI machine this fires during boot with no KLD involved.",
  "evidence": "run.log + panic_triplefault.txt (serial: rc=0 zone[0]=-32 zone[31]=-1, gates 0..31 of cpu1 smashed, triggering int3 immediately followed by the bootloader menu \u2014 triple fault with no dump; post-reset fresh dmesg/uptime confirm the reset); run_fixed.log (same knob, clamped: zone[0]=32, no smash)",
  "kernel_refs": [
    "sys/platform/pc64/icu/icu_abi.c:323",
    "sys/platform/pc64/icu/icu_abi.c:473",
    "sys/platform/pc64/icu/icu_abi.c:495",
    "sys/platform/pc64/icu/icu_abi.c:499",
    "sys/platform/pc64/x86_64/machdep.c:2785",
    "sys/platform/pc64/x86_64/msi.c:433",
    "sys/kern/kern_environment.c:379"
  ],
  "poc_changes": "Boot-config variant written fresh: no ksetenv/initmap hacks \u2014 the knob is set in /boot/loader.conf and a reboot lets the real boot-time initmap() parse it; the trigger module only calls msi_alloc and, if negative vectors were allocated, pins to cpu1 (lwkt_migratecpu) and executes int3 to exercise the smashed gate deterministically (on a fixed kernel zone[0]>=0 and the int3 is skipped).",
  "attempts": 2,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Mon Oct 5 22:59:13 UTC 2026  root@dfbsd:/usr/obj/usr/src/sys/X86_64_NOINV  x86_64",
  "runtime_sec": 600,
  "guest_dirty": 0,
  "build_cmd": "sh build.sh (guest /usr/src/sys/modules/icu_abi_probe, in-tree kmod.mk; see build.log)",
  "run_cmd": "echo hw.icu.msi_start=\"-32\" >> /boot/loader.conf && reboot && kldload /usr/obj/usr/src/sys/modules/icu_abi_probe/icu_msi_start.ko",
  "code_hash": "ad403473a5a8caa5cb05540f81d7d95fcf34a5b8abd0f012de7804f0f6e9579d (icu_msi_start.c)",
  "notes": "ICU twin of DF-1037 (hw.ioapic.msi_start, ioapic_abi.c:753-754 \u2014 identical unvalidated-parse pattern; that pass-1 finding was Info/unverified; this pack verifies the class end-to-end and shows the exception-gate smash, suggesting the ioapic twin deserves re-verification too). Deterministic window choice: cpu1s [-32..-1] aliases cpu0s [160..191] which initmap() leaves UNUSED. The crash is a silent triple fault (cold reset, no dump) \u2014 stronger than a normal panic: no forensic trace survives on the console.",
  "recommended_fix": "Range-check icu_abi_msi_start after the mask in icu_abi_initmap() (if (<0 || >=IDT_HWI_VECTORS) = 0), mirroring DF-1037s suggested fix for the ioapic twin (see fix.diff).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff applied to guest /usr/src/sys/platform/pc64/icu/icu_abi.c, X86_64_NOINV rebuilt and rebooted WITH THE SAME BOOT KNOB still in /boot/loader.conf (kenv hw.icu.msi_start = -32 verified at runtime): the trigger module now gets rc=0 zone[0]=32 zone[31]=63 \u2014 a legal allocation at vectors 32..63 after the clamp \u2014 prints no gate smash, exiting cleanly and returns; guest healthy in the same boot (run_fixed.log). Bad behavior gone with the boot config unchanged.",
  "fix_evidence": "run_fixed.log (rc=0 zone[0]=32 zone[31]=63, allocation not OOB (fixed or clamped kernel?) -- no gate smash, exiting cleanly; uptime continuous)"
}