DF-4231 / trigger.c
/* * DF-4231 trigger -- annotated hardware-precondition probe. * * BUG (source-certain): txp_rx_reclaim() in sys/dev/netif/txp/if_txp.c * stamps the device-fed frame length into the receive cluster mbuf * without any bound: * * if_txp.c:676 m->m_pkthdr.len = m->m_len = rxd->rx_len; * // rxd->rx_len is 'volatile u_int16_t' inside the * // DMA-shared rx descriptor ring (if_txpreg.h:263), * // i.e. fully controlled by the Typhoon firmware * // (RX ring is in sc_ldata, DMA-coherent with the NIC). * * if_txp.c:688-704 (__STRICT_ALIGNMENT is #defined at if_txp.c:85, so * this path is always compiled): * mnew <- MGETHDR; if (m->m_len > MHLEN-2) mnew <- MCLGET // 2048B cluster * m_copydata(m, 0, m->m_pkthdr.len, mtod(mnew, void *)); * * m_copydata() (sys/kern/uipc_mbuf.c:1671) copies * count = min(m->m_len - off, len) = min(rx_len, rx_len) = rx_len * bytes in one bcopy: rx_len bytes READ from the 2048-byte source * cluster and rx_len bytes WRITTEN into the 2048-byte destination * cluster. For any rx_len > MCLBYTES (2048) both the read and the * write run past the end of an mbuf cluster; rx_len is a u_int16_t, so * a faulty/malicious 3c990 firmware can overrun the destination cluster * by up to 63531 bytes (contents = adjacent source-cluster heap bytes). * No KASSERT fires: the source mbuf really does have m_len == rx_len * (the driver itself stamped it), so m_copydata's chain walk succeeds. * * The driver only ever *tells* the firmware to cap frames at * TXP_MAX_PKTLEN (2048, if_txp.c:263/'TXP_CMD_MAX_PKT_SIZE_WRITE'), it * never *verifies* received lengths against it. * * PRECONDITION: a 3Com 3c990 (Typhoon, PCI 10b7:990x) whose firmware * writes rx_len > 2048 into a receive descriptor. This is reachable * only by the device itself (malicious/faulty firmware, or a * passthrough-assigned device attacking the host). No syscall path * can forge it. * * This program just proves, on the live audit guest, that the driver is * present in the running kernel while no 3c990 device exists -- i.e. * the audit guest cannot exercise this path (honest inconclusive). */ #include <stdio.h> #include <stdlib.h> int main(void) { printf("DF-4231: txp_rx_reclaim() trusts NIC-fed rxd->rx_len\n"); printf(" sink : if_txp.c:676 m->m_len = rxd->rx_len (u_int16, DMA)\n"); printf(" sink : if_txp.c:704 m_copydata(m, 0, m->m_pkthdr.len, mtod(mnew))\n"); printf(" bound: none vs MCLBYTES(2048); overflow up to 63531 bytes past\n"); printf(" the destination cluster, plus equal-size OOB read of the\n"); printf(" source cluster.\n"); printf(" needs: 3c990 Typhoon firmware lying about rx_len (hardware)\n"); return (0); } |