# DF-5157 VERDICT — UNTESTED (no ath hardware on the guest); code-trace confidence

**Bottom line:** the LED blink callout (`sc_ledtimer`) can be re-armed *after*
its only teardown cancellation, because `ath_detach()` cancels it in
`ath_stop()` (if_ath.c:2703) **before** draining the tx taskqueue
(`taskqueue_free(sc->sc_tq)`, if_ath.c:1416), and the drained tx-completion
tasks unconditionally call `ath_led_event()` (if_ath.c:4570/4628/4676) which
re-arms the chain (if_ath_led.c:185). No later cancel/drain exists, so the
timer fires into a detached/freed softc and freed HAL: use-after-free.
Not runnable on the audit guest: QEMU has no Atheros NIC emulation.

## Trace (every step cited)

1. **Arming path.** tx completion → `ath_tx_proc_q*()` workers on `sc->sc_tq`
   call `if (sc->sc_softled) ath_led_event(sc, sc->sc_txrix)`
   (if_ath.c:4570, 4628, 4676) — outside any ATH_LOCK section (lock sections
   bracket it at 4554-4556 / 4573-4575). `ath_led_event` (if_ath_led.c:188)
   → `ath_led_blink` (179) → `callout_reset(&sc->sc_ledtimer, on,
   ath_led_off, sc)` (185). `ath_led_off` (166) re-arms `ath_led_done`
   (172). During blinking the callout is thus armed essentially always, with
   periods from the rate map: ledon up to 500ms / ledoff 130ms defaults
   (if_ath.c:6213-6214) or the blink-rate table (6231-6232).
2. **Teardown ordering.** `ath_detach` (if_ath.c:1385): `ath_stop` (1411) →
   inside, gated only by `sc_running` and `!sc->sc_invalid` (2700):
   `callout_cancel(&sc->sc_ledtimer)` (2703, DragonFly branch), then
   `ath_hal_intrset(ah, 0)` (2713) — but a tx task enqueued *before* that is
   still queued. Back in `ath_detach`: `taskqueue_free(sc->sc_tq)` (1416)
   which **runs** all pending tasks synchronously
   (kern/subr_taskqueue.c:151-157: `taskqueue_run(queue, 1)` before
   terminate) → that task's `ath_led_event` re-arms the LED callout **after**
   the cancel. (Note `sc_invalid` is only ever set on the attach-failure path
   at if_ath.c:1375-1378, before any tx can occur — so the `!sc_invalid` gate
   never protects a live teardown; the re-arm hole above is the live path.)
3. **Post-free fire.** `ath_hal_detach` (if_ath.c:1435) frees the HAL;
   the softc is freed by newbus after detach returns (device destruction /
   devclass free on module unload). The re-armed timer fires `ath_led_off`
   (if_ath_led.c:166) `ledon`-ms later: reads `sc->sc_ah` from freed memory,
   calls `ath_hal_gpioset` through the freed HAL (function pointers/state),
   then `callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc)`
   (172) → `ath_led_done` (154) writes `sc->sc_blinking` (159) on freed
   memory. UAF read + indirect call + UAF write.
4. **Callout-core safety net verified (does NOT cover this).** DragonFly's
   `callout_cancel` is synchronous (`_callout_cancel_or_stop(..., sync=1)`,
   kern_timeout.c:1032-1034, wait loop at 907-921) and CANCEL beats a
   handler-side RESET at INPROG-clear (kern_timeout.c:218-229) — so the
   *existing* cancel does quench an in-flight handler chain correctly. The
   hole is purely that a **later** re-arm can happen after it, from the task
   drain. Confirmed no `callout_drain/cancel` for `sc_ledtimer` exists
   anywhere else in if_ath.c (only init at 884/886 and the single stop-site at
   2703/2705).

## What was checked and killed (negative results)

- `ath_led_off`/`ath_led_done` with period 0: the chain always terminates
  (`ath_led_done` never re-arms; if_ath_led.c:154-160); periods are not
  sysctl-settable (only the `sc_ledon` *polarity* is; if_ath_sysctl.c:857).
  No infinite-callout loop.
- `sc_hwmap[rix]` OOB (if_ath_led.c:194): **DF-2152 (known, not re-filed)**;
  the rx-side 0xff sentinel variant is **DF-4315 (known)**. The tx-side
  callers pass `sc->sc_txrix` (u_int8_t rate-table index, if_ath_tx.c:1401).
- GPIO pin handling (`ath_led_config`, if_ath_led.c:127-131): routed through
  the HAL; the ar5416 CfgInput DRV_ALL defect is **DF-5092 (known)** — hal
  file, not re-filed. The root-only `dev.ath.X.ledpin` sysctl accepts
  unvalidated pins (if_ath_sysctl.c:212-228) — HAL-side range handling,
  privileged-only, out of this file's scope.
- `ath_led_config` power-state bracket (119-152) is properly locked
  (ATH_LOCK around set/restore).
- Unlocked `sc_blinking`/`sc_ledoff` data races: benign int/LED-state races
  only (MPSAFE callout, kern_timeout.c internal spinlocks protect the
  callout itself).

## Honest classification

`untested` / impact `none` (not demonstrated on this guest; no ath hardware
exists in QEMU). Analysis confidence: the structural ordering defect is
certain from source; the *exploitability* of the ms-scale race window and the
rare softled prerequisites keep the finding at Low severity / `likely`
confidence overall.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
