# DF-5518 — gpio_acpi: driver detach does not quiesce in-flight GPIO
address-space handler dispatches (UAF of freed softc/provider state)

## What this pack contains

A full production-code harness that brings the *stock* DragonFly
`gpio_intel` + `gpio_acpi` drivers up on the QEMU guest using only planted
ACPI tables (the proven madt-batch/DF-5483 injection vector: kenv
`hint.acpi.0.rsdp=0x40000000` + QEMU `-device loader` blob), a fake
Cherry-View "register" image in the option-ROM window at 0xD0000, and AML
(`TZ0._TMP`) that continuously evaluates a 59-pin GeneralPurposeIo field
so `gpio_acpi_space_handler` is dispatched thousands of times per second —
plus race triggers that detach the live device pair mid-dispatch.

```
planted DSDT \_SB.GPO0 (INT33FF, _UID "2", _CRS: mem 0xD0000+0x8000, irq 5)
  -> gpio_intel0 + gpio_acpi0 attach (stock drivers, unmodified kernel)
  -> OpRegion(GOPR, GeneralPurposeIo) + Connection(GpioIo, 59 pins) + FLD0,59
  -> TZ0._TMP reads FLD0 once, then (once the register image is planted by
     gpoke.ko and the magic pattern matches) hammers @ITER@ more reads
  -> every read: AcpiExReadGpio -> AcpiEvAddressSpaceDispatch
     (interpreter EXITED, evregion.c:392-401) -> gpio_acpi_space_handler
     -> gpioio_alloc_pins -> 59x GPIO_ALLOC_IO_PIN/READ/RELEASE on
        gpio_intel0 (provider lock + MMIO)
```

## Triggers

* `gpo_detach.ko` — lottery race: plain `device_detach`+delete+poison-spray
  while the hammer runs (μs window; 8 attempts, no natural hit — the
  in-flight handler always completed before the frees became visible).
* `gpo_race2.ko` — deterministic: hold the provider `sc->lk`, let the next
  dispatch block mid-handler, then detach (frees softc/device_t/resources),
  then release -> handler resumes on freed state -> **Fatal trap 12,
  `gpio_cherryview_read_pin+0x4c`** (panic.txt / run.race2.1.log).
* `gpo_race3.ko` — fix-validation variant: detach on its own thread,
  delayed lock release (500 ms). On the stock kernel the outcome depends
  on heap-reuse state (observed silent once); on the fixed kernel no
  schedule can fault because detach drains in-flight dispatches.

## Build

    sh build.sh          # iasl on guest, chain assembly on host, kmods on guest
    # race2/race3 are built the same way (see VERDICT.md exact commands)

## Run

    # control (harness verification, no race):
    sh run.sh control
    #   expected: gpio_intel0/gpio_acpi0 attach, gpoke plants the pattern,
    #   `sysctl -n hw.acpi.thermal.tz0.temperature` -> 34.5C (= 3000 + FLD0),
    #   /dev/mem dump at 0xD6000: DBG0=0x55555555 (exact planted 59-bit
    #   pattern through the whole stack), DBG1 = climbing hammer counter.

    # deterministic race (panic):
    #   boot rig; kldload gpoke.ko; sleep 25; kldload gpo_race2.ko
    #   expected: "detach returned 0; freeing lock" followed by
    #   Fatal trap 12, Stopped at gpio_cherryview_read_pin+0x4c  -> db>

    # fix validation:
    #   vm.sh reset with-src; apply fix.diff in guest /usr/src;
    #   make -j6 nativekernel KERNCONF=X86_64_GENERIC; installkernel; reboot;
    #   re-run race3 -> detach returns only after the blocked handler
    #   completed (drain), no trap, guest stays up.

## Success criterion

Stock kernel: kernel panic (fatal trap) in the in-flight GPIO space
handler after detach freed its state. Fixed kernel: same trigger, no
panic; detach blocked until the handler drained and then returned cleanly.
