⬢ DragonFlyBSD Kernel Audit
DF-4233 / trigger.c
← back to finding ↓ download raw
/*
 * 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);
}