DF-4045 / trigger.c
/* * DF-4045 trigger.c - stge_rxeof device-fed length geometry model. * * Hardware precondition (cannot be met on this guest - virtio-net only, * no Sundance/Tamarack TC9021): a faulty or malicious IP1000A/TC9021 DMAs * an RFD status quadword into sc->sc_rdata.stge_rx_ring whose 16-bit * RxDMAFrameLen field (RFD_RxDMAFrameLen(x) = x & 0xffff, if_stgereg.h:167) * lies about the received frame length. The driver applies it with only a * per-IF bound, never a per-buffer bound: * * stge_rxeof, if_stge.c:1606-1608 (FrameEnd descriptor): * mp->m_len = RFD_RxDMAFrameLen(status) - sc->sc_cdata.stge_rxlen; * sc->sc_cdata.stge_rxlen += mp->m_len; * if_stge.c:1623-1629 (only check): * m->m_pkthdr.len = sc->sc_cdata.stge_rxlen; * if (m->m_pkthdr.len > sc->sc_if_framesize) drop; * * sc_if_framesize = if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN (if_stge.c:2013) * and SIOCSIFMTU allows up to STGE_JUMBO_MTU = 9004 (if_stge.c:1261, * if_stgevar.h:55-57), so framesize can be 9022. Every RX buffer is a * plain 2048-byte cluster from stge_newbuf (m_getcl, if_stge.c:2326-2329; * 2046 after the ETHER_ALIGN m_adj when framesize <= 2046, :2335-2336). * * Case A (jumbo, OOB read): MTU=9004, single FrameEnd descriptor with * framelen=9022: mp->m_len = 9022 on a 2048-byte cluster; pkthdr.len * 9022 <= framesize 9022 -> PASSES the only check. Every consumer * trusting m_len/pkthdr.len (BPF tap, m_copydata pulls in the stack) * reads up to 6974 bytes of adjacent kernel heap into a delivered * frame -> info leak to any local user with a socket/BPF on stgeX. * * Case B (any MTU incl. default, negative m_len): k non-FrameEnd * descriptors (each contributing the FULL buffer: 2046/2048 bytes) * then a FrameEnd with framelen < rxlen: mp->m_len goes NEGATIVE, * pkthdr.len = framelen (small, passes). m_copydata * (sys/kern/uipc_mbuf.c:1671,1689) computes * unsigned count = min(m->m_len - off, len); * on the negative-tail mbuf -> count ~ 4GB -> bcopy overflow -> * panic/UB as soon as any consumer walks the chain (BPF attached). * The first mbuf is full-sized, so ether_input's m_len < ETHER_HDR_LEN * guard (if_ethersubr.c) does NOT save this chain. * * This program prints the exact extents implied by the masks/geometry. */ #include <stdio.h> #define RFD_RxDMAFrameLen_MAX 0xffff /* if_stgereg.h:167 */ #define MCLBYTES 2048 #define ETHER_ALIGN 2 #define ETHER_HDR_LEN 14 #define ETHER_CRC_LEN 4 #define STGE_JUMBO_FRAMELEN 9022 /* if_stgevar.h:55 */ int main(void) { int framesize_dflt = 1500 + ETHER_HDR_LEN + ETHER_CRC_LEN; int framesize_jumbo = STGE_JUMBO_FRAMELEN; int buf_dflt = MCLBYTES - ETHER_ALIGN; /* align applied when small */ int buf_jumbo = MCLBYTES; /* no align when jumbo */ printf("DF-4045 stge_rxeof length-trust geometry model\n"); printf("descriptor DMAFrameLen field : 16 bits, max %d\n", RFD_RxDMAFrameLen_MAX); printf("RX cluster (stge_newbuf) : %d bytes (%d with " "ETHER_ALIGN)\n", MCLBYTES, buf_dflt); printf("only bound in driver : pkthdr.len > framesize " "(if_stge.c:1625)\n"); printf("framesize default MTU : %d\n", framesize_dflt); printf("framesize jumbo MTU 9004 : %d (SIOCSIFMTU allows it, " "if_stge.c:1261)\n\n", framesize_jumbo); printf("case A - jumbo MTU, FrameEnd claims framelen = framesize:\n"); for (int k = 0; k <= 3; k++) { int rxlen = k * buf_jumbo; if (rxlen >= framesize_jumbo) break; int mlen = framesize_jumbo - rxlen; int cap = buf_jumbo; if (mlen > cap) printf(" k=%d intermediates: last m_len %d - %d = %d " "on %d-byte cluster -> OOB read %d bytes past " "cluster\n", k, framesize_jumbo, rxlen, mlen, cap, mlen - cap); } printf(" (default MTU: framelen <= 1518 < 2046 -> case A needs " "jumbo MTU)\n\n"); printf("case B - any MTU, framelen < rxlen (negative tail m_len):\n"); { int k = 1, framelen = 100; int mlen = framelen - k * buf_dflt; printf(" k=%d intermediates (rxlen %d), FrameEnd framelen %d " "-> m_len = %d\n", k, k * buf_dflt, framelen, mlen); printf(" pkthdr.len = %d <= framesize -> passes if_stge.c:" "1625; first mbuf full -> survives ether_input guard\n", framelen); printf(" m_copydata: unsigned count = min(%d - off, len) -> " "~4GB bcopy -> panic (uipc_mbuf.c:1689)\n", mlen); } printf("\nprecondition: device-DMA'd RFD status lie " "(faulty/malicious TC9021); guest has no stge(4) device\n"); return 0; } |