DF-4024 / trigger.c
/* * DF-4024 trigger.c - re_rxeof device-fed length geometry model. * * Hardware precondition (cannot be met on this guest - virtio-net only): * a faulty or malicious RTL8169SC/8168/8101E/8125 DMAs a completion * descriptor into sc->re_ldata.re_rx_list whose 14-bit GFRAGLEN field * (RE_RDESC_STAT_GFRAGLEN 0x3FFF, if_rereg.h:139; read by RE_RXBYTES, * if_revar.h:54) lies about the received frame length. The driver * (if_re.c re_rxeof) applies that length to an MCLBYTES (2048) cluster * with no bound: * * single-fragment EOF: if_re.c:1228-1229 * m->m_pkthdr.len = m->m_len = total_len - ETHER_CRC_LEN; * -> m_len up to 16379 on a 2048-byte cluster; every consumer that * trusts m_len (BPF tap at ether_input, m_copydata pulls, MAC/IPv4 * header reads past the first 2KB) reads adjacent kernel heap. * * multi-fragment EOF: if_re.c:1209/1226 * m_pkthdr.len = total_len - 4 may EXCEED the accumulated chain * (k * 2048 + last-frag bytes); m_copydata then walks m_next past * NULL -> KASSERT panic (INVARIANTS) or NULL+0x58 page fault * (GENERIC, KASSERT compiled out) - sys/kern/uipc_mbuf.c m_copydata. * * total_len < 4 sub-case is SAFE: negative m_len is dropped by * ether_input's `m->m_len < sizeof(struct ether_header)' guard * (sys/net/if_ethersubr.c, ether_input) - mirroring ale's * m_devget tot<=0 behavior noted in DF-3989. * * This program prints the exact OOB extents implied by the masks/geometry. */ #include <stdio.h> #define RE_RDESC_STAT_GFRAGLEN 0x00003fff /* if_rereg.h:139 */ #define MCLBYTES 2048 #define ETHER_CRC_LEN 4 #define ETHER_HDR_LEN 14 int main(void) { int max_len = RE_RDESC_STAT_GFRAGLEN; /* device-fed */ int max_mlen = max_len - ETHER_CRC_LEN; /* if_re.c:1228 */ printf("DF-4024 re_rxeof length-trust geometry model\n"); printf("descriptor GFRAGLEN field : 14 bits, max %d\n", max_len); printf("RX cluster (re_newbuf_std) : %d bytes (MCLBYTES)\n", MCLBYTES); printf("single-frag EOF m_len lie : %d - %d = %d\n", max_len, ETHER_CRC_LEN, max_mlen); printf(" -> heap OOB read past cluster: %d bytes\n", max_mlen - MCLBYTES); /* multi-frag: chain of k SOF frags, EOF claims total_len */ for (int k = 1; k <= 7; k++) { int chain = k * MCLBYTES; int excess = max_mlen - chain; if (excess > 0) printf("multi-frag k=%d: pkthdr.len %d > chain %d " "-> m_copydata walks m_next past NULL (+%d)\n", k, max_mlen, chain, excess); } printf("total_len<4 sub-case : negative m_len, dropped by " "ether_input m_len<ETHER_HDR_LEN guard (SAFE)\n"); printf("precondition : device-DMA'd descriptor lie " "(faulty/malicious NIC); guest has no re(4) device\n"); return 0; } |