DF-4233 / trigger.c
/* * DF-4233 trigger -- annotated precondition probe. * * BUG: txp_init() ignores the return value of txp_rxring_fill() and * enables the receiver anyway: * * if_txp.c:1110 txp_stop(sc); // txp_rxring_empty() FREES all * // 256 rx clusters and NULLs * // sd->sd_mbuf, but the DMA * // ring's rb_paddrlo entries * // keep pointing at the * // FREED clusters * if_txp.c:1127 txp_rxring_fill(sc); // return value IGNORED * if_txp.c:1129 txp_command(TXP_CMD_TX_ENABLE, ...); * if_txp.c:1130 txp_command(TXP_CMD_RX_ENABLE, ...); // RX starts * * txp_rxring_fill() (if_txp.c:1039-1070) bails out with ENOBUFS at the * first MGETHDR/MCLGET(M_NOWAIT) failure and -- on failure -- does NOT * update hv_rx_buf_write_idx, which therefore still holds its stale * value from the previous session (set to (RXBUF_ENTRIES-1)*16 = * "255 buffers available" by the last successful fill/reclaim). * * Consequences once traffic flows, in order: * a) the firmware consumes "available" rx buffers whose * rb_paddrlo point at mbuf clusters freed by txp_rxring_empty() * => the NIC DMAs attacker-supplied frame content into freed * kernel heap clusters (use-after-free WRITE with attacker- * controlled data => heap corruption; on DragonFly clusters are * 2KB slabs in the mbuf zone, so adjacent objects get smashed). * b) when the firmware posts the completion rx descriptor for such a * buffer, txp_rx_reclaim() does * m = sd->sd_mbuf; // NULL for the unfilled prefix * m->m_pkthdr.len = ... // if_txp.c:676 => NULL deref panic * * PRECONDITION: any MCLGET(M_NOWAIT) failure during ifconfig txp0 up -- * i.e. transient kernel memory pressure (remotely seedable by flooding * the box) -- followed by an inbound frame for the stale prefix of the * buffer ring. Needs real 3c990 hardware for the DMA half; the NULL * deref at :676 needs only the descriptor walk, still hardware-fed. */ #include <stdio.h> int main(void) { printf("DF-4233: txp_init ignores txp_rxring_fill() failure\n"); printf(" if_txp.c:1127 fill rc ignored; :1130 RX_ENABLE anyway\n"); printf(" stale rb_paddrlo => firmware DMA into freed clusters\n"); printf(" unfilled sd->sd_mbuf==NULL => NULL deref at :676\n"); printf(" needs: MCLGET failure + inbound frame on 3c990 hw\n"); return (0); } |