DF-3849 / trigger.c
/* * trigger.c - arithmetic replica of the dc_rxeof() length-trust defect * (sys/dev/netif/dc/if_dc.c:2511-2604). * * dc_rxeof() takes the frame length from the NIC-DMA'd RX descriptor * status word with NO bounds check against the posted mbuf cluster: * * total_len = DC_RXBYTES(rxstat); // if_dc.c:2523 * ... * total_len -= ETHER_CRC_LEN; // if_dc.c:2568 * if (dc_quick && dc_newbuf(sc, i, NULL) == 0) { * m->m_pkthdr.len = m->m_len = total_len; // if_dc.c:2583 * } else { * m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, * total_len + ETHER_ALIGN, 0, ifp); // :2590 * } * * The only sanity gate before this is the RXERR branch (if_dc.c:2548-2565), * which reads error bits from the SAME device-produced status word, and * explicitly tolerates GIANT (0x80) frames without other error bits * ("could be vlans"). * * The posted cluster (dc_newbuf, if_dc.c:2321-2343) is MCLBYTES with * m_adj(sizeof(u_int64_t)): 2048 - 8 = 2040 bytes usable. DC_RXSTAT_RXLEN * is a 14-bit field (if_dcreg.h:415), so a device-written descriptor with * RXERR clear and FRAMESZ = 16383 produces m_len = 16379 on a 2040-byte * cluster: a ~14 KB heap over-read when the stack consumes the mbuf * (bpf taps / ether_input / socket copies). FRAMESZ < 4 wraps total_len * negative (assigned to m_len, an int). * * This program executes the exact integer math with the exact macros from * if_dcreg.h. Compile: cc -O2 -o trigger trigger.c */ #include <stdio.h> #define DC_RXSTAT_RXLEN 0x3FFF0000 /* if_dcreg.h:415 */ #define DC_RXBYTES(x) ((x & DC_RXSTAT_RXLEN) >> 16) /* :418 */ #define ETHER_CRC_LEN 4 #define MCLBYTES 2048 /* sys/sys/param.h */ #define POSTED_USABLE (MCLBYTES - 8) /* m_adj(sizeof(u_int64_t)) */ static void feed(unsigned rxstat) { int total_len; total_len = DC_RXBYTES(rxstat); /* if_dc.c:2523 */ total_len -= ETHER_CRC_LEN; /* if_dc.c:2568 */ printf("rxstat=0x%08x FRAMESZ=%5u -> m_len=%-6d on a %d-byte cluster", rxstat, DC_RXBYTES(rxstat), total_len, POSTED_USABLE); if (total_len > POSTED_USABLE) printf(" => OOB READ %d bytes past the cluster (%.1f KB)", total_len - POSTED_USABLE, (total_len - POSTED_USABLE) / 1024.0); if (total_len < 0) printf(" => NEGATIVE m_len (unsigned ~%u): stack OOB walk", (unsigned)total_len); printf("\n"); } int main(void) { printf("dc_rxeof() length-trust arithmetic (if_dc.c:2523,2568,2583)\n"); /* legal frame, RXERR clear, whole frame */ feed(1518 << 16); /* VLAN frame (the GIANT tolerance rationale) */ feed(1522 << 16); /* crafted: FRAMESZ 2048 (just past cluster) */ feed(2048 << 16); /* crafted: FRAMESZ 4096 */ feed(4096 << 16); /* crafted: 14-bit field max, RXERR clear */ feed(16383 << 16); /* crafted: max + FIRSTFRAG|LASTFRAG set */ feed((16383 << 16) | 0x0300); /* crafted: FRAMESZ 2 -> total_len underflow */ feed(2 << 16); return (0); } |