DF-3967 / trigger.c
/* * 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; } |