# DF-5294 VERDICT — post-free ratectl callout/task cycle after vap deletion

**Status: inconclusive (hardware-gated).  Bug: real, protocol-proven.**

## Reproduction path (in-kernel)

1. Attach rum(4), STA vap with automatic rate adaptation
   (`tp->ucastrate == IEEE80211_FIXED_RATE_NONE` at S_RUN arms the callout,
   if_rum.c:989-991 → rum_ratectl_start :3085-3094).
2. The callout fires every `hz`; `rum_ratectl_timeout` (:3096-3104) enqueues
   `rvp->ratectl_task`; `rum_ratectl_task` (:3106-3142) ends with the
   UNCONDITIONAL `usb_callout_reset` at :3140.
3. `ifconfig wlan0 destroy` → `rum_vap_delete`:
   - :716 `usb_callout_drain` returns once the callout is neither pending
     nor executing — a task enqueued by the last fire is still queued;
   - :717 `taskqueue_drain` waits for that task; the task RE-ARMS the
     callout at :3140 on its way out (no guard to stop it);
   - :718-:719 `ratectl_deinit` + `ieee80211_vap_detach` → `ether_ifdetach`
     + `ieee80211_stop` → `rum_newstate` → `usb_callout_stop` (:934) — the
     stop is at the END of this window and cannot un-queue a task whose
     callout fired inside it;
   - :720 `kfree(rvp, M_80211_VAP)`.
4. A fire inside the :718-:719 window (or a task still queued at :720)
   executes `rum_ratectl_timeout`/`rum_ratectl_task` on the freed `rum_vap`:
   `ieee80211_runtask()` writes into freed memory on every enqueue,
   `vap->iv_ic`/`vap->iv_bss` are dereferenced from freed heap, and :3140
   re-arms the callout on the freed object — the cycle self-perpetuates.

Objective deviation from both siblings in the same tree: if_run.c:2497 and
if_mtw.c:2401 guard every re-arm with `ratectl_run`, cleared in vap_delete
(if_run.c:1017, if_mtw.c:895) BEFORE the drains. rum has neither the guard
nor the clear.

## Why inconclusive on this guest
No RT2573 hardware ⇒ no vap lifecycle.  Structural evidence only.

## Simulated evidence
`rum_ratectl_drain.c` models callout thread (tick), taskqueue worker (with
the :3140 re-arm, guarded or not), and a destroyer running the exact
shipped sequence (drain callout → drain task → detach-window delay →
kfree → post-free observation), timings scaled 1s→4ms.
Guest, 3×10 s per mode: shipped → 360 destroy cycles with 115/108/125
post-free callout fires and 111/108/127 post-free task runs; guarded →
0/0.  The interleaving is reachable whenever the detach window spans one
tick — `ether_ifdetach`'s netisr round-trips make that realistic under load.

## Exploit chain (if hardware present)
`kfree`d `rum_vap` (M_80211_VAP zone, ~0x2c0) reallocated by an attacker
with vap-creation control → `ieee80211_runtask` enqueue writes
(`ta_pending`/`ta_link`) into the reallocated object at fixed offsets +
`rum_ratectl_task` consumes attacker-shaped `vap->iv_ic` pointer →
arbitrary-ish kernel deref/write path → memory corruption → panic to
potential privesc.  (Requires root to destroy the vap — the primitive is
root-triggered corruption, not an unpriv escalation.)

## Fix
`sc->ratectl_run` flag: set in `rum_ratectl_start`, cleared in
`rum_newstate` beside the existing callout stop, cleared in
`rum_vap_delete` BEFORE the drains, and the :3140 re-arm made conditional
(fix.diff touches if_rum.c + if_rumvar.h, `git apply --check` clean).
This is exactly the if_run/if_mtw protocol.  Kernel fix validation
hardware-gated: not_testable.

## Kernel references
- sys/bus/u4b/wlan/if_rum.c:3140 (unguarded re-arm)
- sys/bus/u4b/wlan/if_rum.c:709-721 (drain order; kfree)
- sys/bus/u4b/wlan/if_rum.c:934 (late callout_stop in newstate)
- sys/bus/u4b/wlan/if_rum.c:989-991 (arm at S_RUN)
- sys/bus/u4b/wlan/if_run.c:2497-2498, if_mtw.c:2401-2402 (the guard rum lacks)
- sys/bus/u4b/wlan/if_run.c:1017, if_mtw.c:895 (clear-before-drain)
- sys/netproto/802_11/wlan/ieee80211.c:752 ff. (vap_detach → ieee80211_stop
  AFTER rum's drains, BEFORE kfree)
- sys/bus/u4b/usbdi.h:411 (usb_callout_drain == callout_drain)

## 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.
