DF-4066 / trigger.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | /* * DF-4066 trigger/geometry model (runs on the audit guest; userland). * * Bug modeled (source-certain, hardware-gated): * ti_rxeof() reads the return-ring descriptor written by the Tigon * firmware over PCI DMA and uses its uint16_t ti_idx field * (sys/dev/netif/ti/if_tireg.h:711) directly as an array index: * * rxidx = cur_rx->ti_idx; if_ti.c:1638 * m = sc->ti_cdata.ti_rx_jumbo_chain[rxidx]; if_ti.c:1648 * sc->ti_cdata.ti_rx_jumbo_chain[rxidx] = NULL; if_ti.c:1649 * m = sc->ti_cdata.ti_rx_mini_chain[rxidx]; if_ti.c:1662 * sc->ti_cdata.ti_rx_mini_chain[rxidx] = NULL; if_ti.c:1663 * m = sc->ti_cdata.ti_rx_std_chain[rxidx]; if_ti.c:1676 * sc->ti_cdata.ti_rx_std_chain[rxidx] = NULL; if_ti.c:1677 * * ... with NO bounds check against the array sizes * (if_tireg.h:1097-1102): * ti_rx_std_chain [TI_STD_RX_RING_CNT = 512] * ti_rx_jumbo_chain [TI_JUMBO_RX_RING_CNT = 256] * ti_rx_mini_chain [TI_MINI_RX_RING_CNT = 1024] * * Consequences per out-of-range index: * (1) OOB READ of a kernel pointer that is then used as `struct mbuf *m`: * if_ti.c:1690 does m->m_pkthdr.len = m->m_len = cur_rx->ti_len * (two stores through the wild pointer) and if_ti.c:1706 * ifp->if_input(ifp, m, ...) feeds the wild mbuf to the network * stack (type confusion: whatever the OOB read picked up -- e.g. * ti_jslots[].ti_buf / .ti_sc kernel pointers -- is treated as an * mbuf). * (2) OOB WRITE: chain[rxidx] = NULL stores 8 zero bytes at * base + 8*rxidx, up to +512KB past the array (rxidx <= 65535), * inside/past struct ti_softc (device softc, kernel heap). * * This program models the softc chain-data layout (if_tireg.h:1096-1105) * and prints, for each receive path and a sample of DMA'd indices, where * the OOB read / OOB NULL write land. It does NOT touch kernel memory: * exercising the real path needs a Tigon/Tigon2 NIC whose firmware emits * an out-of-range descriptor index (faulty or malicious peripheral / * DMA tampering -- same attacker model as DF-3604/DF-3989/DF-4024/ * DF-4045/DF-4055). */ #include <stdio.h> #include <stdint.h> /* if_tireg.h:395-411 */ #define TI_STD_RX_RING_CNT 512 #define TI_JUMBO_RX_RING_CNT 256 #define TI_MINI_RX_RX_RING_CNT 1024 #define TI_MINI_RX_RING_CNT 1024 #define TI_TX_RING_CNT 512 #define TI_JSLOTS 384 /* if_tireg.h:1097-1105: struct ti_chain_data, 64-bit layout, all pointers 8B */ struct region { const char *name; size_t bytes; }; static struct region cdata_layout[] = { { "ti_tx_chain[512] ", TI_TX_RING_CNT * 8 }, { "ti_rx_std_chain[512] ", TI_STD_RX_RING_CNT * 8 }, { "ti_rx_jumbo_chain[256]", TI_JUMBO_RX_RING_CNT * 8 }, { "ti_rx_mini_chain[1024]", TI_MINI_RX_RING_CNT * 8 }, { "ti_jslots[384] (32B ea; ti_sc/ti_buf/ti_inuse/ti_slot)", TI_JSLOTS * 32 }, { "ti_jumbo_buf ptr ", 8 }, { "-- end of struct ti_chain_data (softc continues: ti_rdata ptr, ifmedia, arpcom fields...) --", 0 }, }; static void locate(const char *path, uint32_t count, size_t arr_base, uint32_t rxidx) { size_t off = (size_t)rxidx * 8; /* element offset from indexed array */ size_t abs_off = arr_base + off; /* offset within struct ti_chain_data */ size_t base = 0, i; printf(" %s rxidx=%-6u -> element offset +%7zu (max in-bounds +%zu)", path, rxidx, off, ((size_t)count - 1) * 8); if (rxidx < count) { printf(" in-bounds\n"); return; } printf(" OOB by %u elements\n", rxidx + 1 - count); for (i = 0; cdata_layout[i].bytes; i++) { if (abs_off >= base && abs_off < base + cdata_layout[i].bytes) { printf(" 8-byte NULL write lands inside %s (+%zu within that region)\n", cdata_layout[i].name, abs_off - base); return; } base += cdata_layout[i].bytes; } printf(" 8-byte NULL write lands %zu bytes past struct ti_chain_data " "(further softc fields / adjacent kernel heap)\n", abs_off - base); } /* array bases within struct ti_chain_data (if_tireg.h:1097-1105, 64-bit) */ #define BASE_TX ((size_t)0) #define BASE_STD (BASE_TX + TI_TX_RING_CNT * 8) #define BASE_JUMBO (BASE_STD + TI_STD_RX_RING_CNT * 8) #define BASE_MINI (BASE_JUMBO + TI_JUMBO_RX_RING_CNT * 8) int main(void) { uint32_t sidx[] = { 511, 512, 513, 640, 1023, 2047, 4095, 65535 }; uint32_t jidx[] = { 255, 256, 257, 511, 1024, 65535 }; uint32_t midx[] = { 1023, 1024, 2048, 65535 }; unsigned i; printf("DF-4066 model: ti_rxeof OOB via NIC-DMA'd uint16_t ti_idx " "(if_ti.c:1638,1648-77)\n"); printf("chain arrays (if_tireg.h:1099-1102): std=512 jumbo=256 mini=1024; " "idx field u16 (if_tireg.h:707/711)\n\n"); printf("STD path (array ti_rx_std_chain[512], if_ti.c:1676-1677):\n"); for (i = 0; i < sizeof(sidx)/sizeof(sidx[0]); i++) locate("std ", TI_STD_RX_RING_CNT, BASE_STD, sidx[i]); printf("\nJUMBO path (array ti_rx_jumbo_chain[256], if_ti.c:1648-1649):\n"); for (i = 0; i < sizeof(jidx)/sizeof(jidx[0]); i++) locate("jumbo", TI_JUMBO_RX_RING_CNT, BASE_JUMBO, jidx[i]); printf("\nMINI path (array ti_rx_mini_chain[1024], if_ti.c:1662-1663):\n"); for (i = 0; i < sizeof(midx)/sizeof(midx[0]); i++) locate("mini ", TI_MINI_RX_RING_CNT, BASE_MINI, midx[i]); printf("\nPrimitives per out-of-range rxidx:\n"); printf(" READ : m = chain[rxidx] (if_ti.c:1648/1662/1676)\n"); printf(" WRITE: chain[rxidx] = NULL (if_ti.c:1649/1663/1677) " "-> 8 zero bytes at base+8*rxidx, rxidx<=%u -> up to +%u KB\n", 65535u, (unsigned)(65535 * 8 / 1024)); printf(" WRITE: m->m_pkthdr.len = m->m_len = cur_rx->ti_len " "(if_ti.c:1690) -> two stores through the OOB-read pointer,\n" " m_len at fixed offset +8 of the wild 'mbuf', value = " "NIC-chosen 16-bit ti_len\n"); printf(" USE : ifp->if_input(ifp, m, NULL, -1) (if_ti.c:1706) -> " "wild-mbuf type confusion into ether_input\n"); printf("\nRC_DF4066_MODEL=0\n"); return 0; } |