DF-2307 / verdict.json
{ "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" } |