⬢ DragonFlyBSD Kernel Audit
DF-3967 / trigger.c
← back to finding ↓ download raw
/*
 * DF-3967 trigger sketch — rt2661_tx_intr STA_CSR4 completion desync
 * ==================================================================
 * rt2661_tx_intr() drains the device register STA_CSR4 (rt2661.c:896-960):
 *
 *   val = RAL_READ(sc, RT2661_STA_CSR4);
 *   if (!(val & RT2661_TX_STAT_VALID)) break;
 *   qid = RT2661_TX_QID(val);                        // (val>>8)&0xf  -> 0..15
 *   txq = (qid <= 3) ? &sc->txq[qid] : &sc->mgtq;    // qid 5..15 -> mgtq!
 *   data = &txq->data[txq->stat];                    // driver cursor, not HW
 *   m = data->m; data->m = NULL; ni = data->ni; data->ni = NULL;
 *   if (ni == NULL) continue;                        // m LEAKED here
 *   ...
 *   txq->queued--;                                   // no floor at 0
 *   if (++txq->stat >= txq->count) txq->stat = 0;
 *   ieee80211_tx_complete(ni, m, error);             // frees m
 *
 * The driver assumes the device returns results in the exact order the
 * driver posted them (stat cursor) and one per posted frame. A device that
 * (a) reports qid 5..15 (garbage) attributes the result to the MGT ring;
 * (b) emits more VALID entries than frames posted drives `queued` negative —
 * the ring-full gate `sc->txq[ac].queued >= RT2661_TX_RING_COUNT - 1`
 * (rt2661.c:1687) then never trips, `cur` laps `next` and the post path
 * overwrites descriptors/data slots the device still owns;
 * (c) emits a result while the stat cursor points at a still-in-flight slot
 * hands that slot's m to ieee80211_tx_complete (freed) while the device is
 * still DMAing it -> mbuf UAF write window.
 *
 * Also: the `ni == NULL` early-continue drops `m` without m_freem (leak).
 *
 * Device-gated (malicious/buggy NIC), same trust class as DF-3749 (rt2860
 * TX-stat drain UAF) but a different mechanism (cursor desync vs stale map).
 *
 * Trigger on hardware: patch device firmware (or PCIe emulate) to post one
 * spurious TX_STAT_VALID entry per real one; run TCP traffic; observe
  queued underflow via ddb (`p sc->txq[0].queued`) and eventual m reuse.
 */
int main(void) { return 0; }