DF-4067 / trigger.c
/* * DF-4067 trigger/geometry model (runs on the audit guest; userland). * * Bug modeled (source-certain, hardware-gated): * ti_rxeof() sets the delivered mbuf's length from the NIC-DMA'd * return-descriptor field ti_len (uint16_t, if_tireg.h:710) with no * bound against the buffer actually backing the descriptor: * * m->m_pkthdr.len = m->m_len = cur_rx->ti_len; if_ti.c:1690 * * Buffers per ring: * std : m_getcl cluster, MCLBYTES = 2048 (param.h:497), then * m_adj(m, ETHER_ALIGN=2) -> usable 2046 (if_ti.c:694-705); * firmware-legit max is rcb->ti_max_len = TI_FRAMELEN = 1518 * (if_ti.c:1279, tireg.h:821). * mini : MGETHDR mbuf, data area MHLEN (<= ~480), m_adj(2) * (if_ti.c:730-741); rcb max_len = MHLEN - ETHER_ALIGN * (if_ti.c:1305). * jumbo : driver jumbo pool slot TI_JLEN = roundup(TI_JUMBO_FRAMELEN * + 2, 8) = 9024, data at +2 -> usable 9022 (if_ti.c:799-802, * tireg.h:1048-1050,822). * * A descriptor claiming ti_len > usable makes the mbuf claim more bytes * than its cluster holds; every consumer trusting m_len/m_pkthdr.len * (BPF tap, ether_input, protocol m_copydata/pullup, socket receive) * reads PAST the buffer: kernel-heap disclosure into delivered frames * (DF-3604 bce / DF-3989 ale / DF-4024 re / DF-4045 stge / DF-4055 et * family) or a page fault on unmapped tail. The std path alone allows * m_len up to 65535 on a 2048-byte cluster = 63489 bytes of OOB read. * * This program prints the per-ring OOB read extents for the full 16-bit * ti_len range. It does NOT touch kernel memory: the real path needs a * Tigon NIC (faulty/malicious firmware or DMA tampering) that writes a * lying ti_len into the return ring. */ #include <stdio.h> #define MCLBYTES 2048 /* sys/sys/param.h:497 */ #define ETHER_ALIGN 2 /* sys/net/ethernet.h:41 */ #define TI_FRAMELEN 1518 /* if_tireg.h:821 */ #define TI_JUMBO_FRAMELEN 9018 /* if_tireg.h:822 */ #define TI_JRAWLEN (TI_JUMBO_FRAMELEN + ETHER_ALIGN) /* 9020 */ #define TI_JLEN ((TI_JRAWLEN + 7) & ~7) /* 9024 */ #define MHLEN_GUEST 224 /* conservative lower bound; DFly MSIZE=512, sizeof(m_hdr)+sizeof(pkthdr) ~= 288 -> MHLEN ~= 224 (exact value boot-dependent) */ struct ringinfo { const char *ring; unsigned usable; /* bytes actually addressable from m_data */ unsigned legit_max; /* firmware-legit max (rcb->ti_max_len) */ unsigned claimed_max;/* NIC-claimable (u16) */ }; static struct ringinfo rings[] = { { "std (MCLBYTES cluster)", MCLBYTES - ETHER_ALIGN, TI_FRAMELEN, 65535 }, { "mini (MHLEN mbuf hdr area)", MHLEN_GUEST - ETHER_ALIGN, MHLEN_GUEST - ETHER_ALIGN, 65535 }, { "jumbo (TI_JLEN slot)", TI_JLEN - ETHER_ALIGN, TI_JUMBO_FRAMELEN, 65535 }, }; int main(void) { unsigned i; printf("DF-4067 model: ti_rxeof trusts NIC-DMA'd u16 ti_len " "(if_ti.c:1690) with no bound vs buffer size\n\n"); for (i = 0; i < sizeof(rings)/sizeof(rings[0]); i++) { printf("%-28s usable=%-5u firmware-legit max=%-5u " "claim max=%u -> OOB read up to %u bytes past buffer\n", rings[i].ring, rings[i].usable, rings[i].legit_max, rings[i].claimed_max, rings[i].claimed_max - rings[i].usable); } printf("\nstd ring detail: m_len=65535 on 2048B cluster -> " "m_copydata/BPF read 65535 bytes starting at data(+2): " "%u bytes OOB (16x the cluster)\n", 65535u - (MCLBYTES - ETHER_ALIGN)); printf("jumbo ring detail: m_len=65535 on 9024B slot -> %u bytes OOB; " "slot pool block = TI_JSLOTS*TI_JLEN = %u bytes, so claims beyond " "the block read past the contigmalloc region\n", 65535u - (TI_JLEN - ETHER_ALIGN), 384u * TI_JLEN); printf("\nRC_DF4067_MODEL=0\n"); return 0; } |