DF-5465 / verdict.json
{ "finding_id": "DF-5465", "status": "reproduced", "reproduced": true, "impact": "panic", "confidence": "certain", "verdict": "REPRODUCED on a production-behaviour kernel (X86_64_NOINV): icu_abi_legacy_intr_config() (icu_abi.c:366-397) validates its driver-supplied irq only with a KKASSERT (icu_abi.c:373) that compiles to nothing without INVARIANTS, and returns void with no error path. With irq=1000 it silently performs the OOB read-modify-write map->im_flags |= ICU_IMF_CONF (icu_abi.c:379) and im_trig write (:389) at &icu_irqmaps[0][1000] (+16000 bytes, cpu5s map row) and feeds the wild irq to elcr_write_trigger (elcr.c:70-92, KASSERT-only guard, 1<<irq UB shift, outb to the ELCR port). With irq=-100000000 it dies at exactly the predicted instruction: Fatal trap 12 ... fault virtual address = 0xffffffff2238a42c (supervisor write data) ... Stopped at icu_abi_legacy_intr_config+0x25: orl $0x1,0xc(%rbx) \u2014 the im_flags RMW at a wild negative index. On INVARIANTS kernels the same call panics at the assert. Fix (runtime irq bounds + LEGACY type check -> early return) validated on a rebuilt kernel: both steps return without write and without panic, guest healthy.", "exploit_chain": "driver calls bus_config_intr(irq, trig, pola) / machintr_legacy_intr_config (sys/sys/machintr.h:103) with an out-of-range irq (probe bug, device hint, or an MSI vector) -> icu_abi.c:374 &icu_irqmaps[0][irq] with unbounded 16-byte stride -> :379 |= RMW and :389 enum store at driver-chosen offset (silent corruption of adjacent per-CPU map rows for moderate values; kernel-mode page fault for wild values) -> :396 elcr_write_trigger(irq) with the same wild index. Requires already-privileged driver-facing reach, hence Low.", "evidence": "run.log + panic_wild_irq.txt (step1 RETURNED WITHOUT ERROR, step2 Fatal trap 12 ... Stopped at icu_abi_legacy_intr_config+0x25: orl $0x1,0xc(%rbx), fault VA arithmetic = icu_irqmaps 0xffffffff81a00420 - 0x5F5E1000 + 0xc); run_fixed.log (both steps return, no panic)", "kernel_refs": [ "sys/platform/pc64/icu/icu_abi.c:373", "sys/platform/pc64/icu/icu_abi.c:374", "sys/platform/pc64/icu/icu_abi.c:379", "sys/platform/pc64/icu/icu_abi.c:389", "sys/platform/pc64/icu/icu_abi.c:396", "sys/platform/pc64/icu/elcr.c:78", "sys/sys/machintr.h:103" ], "poc_changes": "Rig written fresh (family pattern from DF-5352s legacy_crash.c, adapted from the ioapic struct to MachIntrABI_ICU and to the ICU code path: two-step single module \u2014 moderate index proves the silent OOB write survives, wild negative index proves the panic; no ioapic gsi_search involved on the ICU path so the fault itself is the crasher).", "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": 400, "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": "kldload /usr/obj/usr/src/sys/modules/icu_abi_probe/icu_legacy_probe.ko", "code_hash": "eb3f19c70107562f539f96163d37f23776b4b6fd74e5c70d44674ad6d5ff733b (icu_legacy_probe.c)", "notes": "The faulting instruction disassembly (orl $0x1,0xc(%rbx)) matches struct icu_irqmaps im_flags at offset 0xc of a 16-byte element \u2014 byte-level confirmation of the static analysis. Same-class bullets folded in: intr_enable/disable/setup/teardown KASSERT-only bounds (icu_abi.c:178/196/263/287, fixed too) and icu_abi_msi_map type/base KASSERTs only (icu_abi.c:628-634; msi_map(-1,...) is values-only, unfixed by design \u2014 no memory unsafety).", "recommended_fix": "Add runtime bounds (irq < 0 || irq >= ICU_HWI_VECTORS) and LEGACY type check returning early in icu_abi_legacy_intr_config(); same runtime bounds in intr_enable/disable/setup/teardown (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; identical PoC re-run: step1 returns at the bounds/type check before any write (no OOB RMW), step2 returns without panic (was: Fatal trap 12), guest healthy afterwards in the same boot (run_fixed.log). Legal legacy configuration of irq 0..15 LEGACY maps is unaffected.", "fix_evidence": "run_fixed.log (step1 RETURNED WITHOUT ERROR with no write performed; step2 RETURNED WITHOUT PANIC; uname/uptime OK)" } |