⬢ DragonFlyBSD Kernel Audit
DF-2307 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2307",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "REPRODUCED deterministically on a stock-kernel planted-ACPI boot (RSDP->RSDT->FACP->DSDT chain at 0x40000000 per the DF-5518 injection family, DSDT carrying \\_SB.GPO0 + _AEI GpioInt pin 2 + _E02): the rig detaches the boot-attached gpio pair, plants the Cherry-View pin-2 CTL0/CTL1 words (boot-time planting cannot survive firmware), re-attaches through the REAL newbus path -- gpio_cherryview_map_intr accepts (pin=2 trigger=1 polarity=0 ctrl0=0x40008200) and intrmaps[4] is armed with the REAL gpio_acpi_aei_handler and the REAL info pointer -- then queues a blocker on the single-kthread OSL_GPE_HANDLER queue (OsdSchedule.c:100-141 FIFO), raises the pin-2 IS bit and runs the REAL ISR dispatch (lockmgr sc->lk + sc->fns->intr == gpio_cherryview_intr) so aei_handler AcpiOsExecute()s the REAL gpio_acpi_handle_event(info) behind the blocker, detaches gpio_acpi0 (real device_detach -> gpio_acpi_unmap_aei: TEARDOWN/FREE_INTR + memset + kfree with the item still queued; the in-source XXX at :452 admits no draining), and releases the blocker: Fatal trap 12, fault virtual address 0x28 = device_get_parent(NULL) from handle_event info->dev at gpio_acpi.c:317 on the freed/zeroed info. Two clean stock runs panicked identically. Guest dies. Root/physical-adjacency gated (kmod trigger replaces the physical GPIO line assert).",
  "exploit_chain": "planted DSDT _AEI GpioInt pin2 -> real map_intr/SETUP_INTR arming -> deferred AcpiOsExecute item queued -> unmap_aei frees infos array without draining -> deferred handle_event derefs freed info -> Fatal trap 12 (panic; memory-safety UAF read with garbage-pointer potential if the slab is reused)",
  "evidence": "run.log + panic.txt: full console choreography (intrmaps[4] armed, ISR dispatched, detach returned, blocker released, Fatal trap 12 fault 0x28) and the trap dump",
  "kernel_refs": [
    "sys/bus/gpio/gpio_acpi/gpio_acpi.c:441",
    "sys/bus/gpio/gpio_acpi/gpio_acpi.c:450",
    "sys/bus/gpio/gpio_acpi/gpio_acpi.c:452",
    "sys/bus/gpio/gpio_acpi/gpio_acpi.c:453",
    "sys/bus/gpio/gpio_acpi/gpio_acpi.c:455",
    "sys/bus/gpio/gpio_acpi/gpio_acpi.c:310",
    "sys/bus/gpio/gpio_acpi/gpio_acpi.c:317",
    "sys/bus/gpio/gpio_acpi/gpio_acpi.c:347",
    "sys/dev/acpica/Osd/OsdSchedule.c:100"
  ],
  "poc_changes": "Built the whole rig: dsdt_aei.dsl (+iasl fix: GpioInt needs the DebounceTimeout word), gen_aei.py chain builder (reuses the DF-5518 proven FADT/MADT clone), aei_race.c trigger (detach->plant->re-attach choreography because the register image cannot survive firmware to boot-time attach; blocker-on-GPE-queue interposition; real ISR body invocation). Register-image arithmetic fixed after the first attempt (GPIOEN|GPIOCFG=0x200 is 0x8200 not 0xA00).",
  "attempts": 6,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Wed Oct 21 11:04:42 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/BUSAUDIT x86_64 (stock GENERIC + device targ; INVARIANTS)",
  "runtime_sec": 300,
  "guest_dirty": 1,
  "build_cmd": "iasl dsdt_aei.dsl ; python3 gen_aei.py chain dsdt_aei.aml aei_chain.bin ; make SYSDIR=/usr/src/sys (aei_race.ko)",
  "run_cmd": "boot with QEMU -device loader (chain at 0x40000000) + kenv hint.acpi.0.rsdp ; kldload aei_race.ko",
  "code_hash": "07e6e48ea8ff33dd60df1b97a0e9955acae8712c66c5dfa96cb156d8d1adba81",
  "notes": "Fix validation ran the identical choreography on the fix kernel: two fresh-boot runs survived (drain held the detach for its grace period, leaked the info instead of freeing it, handle_event ran on intact memory, STILL ALIVE). One anomalous panic occurred on a rig-loaded boot and one further panic was traced to the rig itself unloading aei_race.ko while acpi_task was still inside the blocker text (exec-fault backtrace captured); neither reproduces on canonical fresh-boot runs. Disassembly of the fix kernel confirms both the atomic inflight accounting and the NULL-check are in the running binary.",
  "recommended_fix": "inflight refcount (inc before AcpiOsExecute, dec+wakeup in handle_event) + bounded wait in unmap_aei degrading to leaking the info (fix.diff).",
  "fix_status": "fixed",
  "fix_validation": "fix kernel canonical fresh-boot runs: no panic (STILL ALIVE), drain visible in disassembly and behavior; caveat: one anomalous boot panicked (backtrace lost to reset) -- see notes; rig kldunload race identified and documented"
}