DF-4068 / trigger.c
/* * DF-4068 trigger/termination model (runs on the audit guest; userland). * * Bug modeled (source-certain, hardware-gated): * All three ti_intr() completion walks terminate on raw equality between * a driver-side consumer index (uint16_t, kept inside the ring modulus by * TI_INC(x,y) = (x+1)%y, if_tireg.h:965) and a 32-bit index word the * Tigon firmware DMA-writes into host memory (struct ti_producer.ti_idx, * if_tireg.h:485-488; the NIC owns ti_return_prodidx_r / ti_tx_considx_r * / ti_ev_prodidx_r inside the contigmalloc'd ti_ring_data): * * ti_rxeof: while (sc->ti_rx_saved_considx != + * sc->ti_return_prodidx.ti_idx); if_ti.c:1629 * ti_txeof: while (sc->ti_tx_saved_considx != * sc->ti_tx_considx.ti_idx); if_ti.c:1729 * ti_handle_events: while (sc->ti_ev_saved_considx != * sc->ti_ev_prodidx.ti_idx); if_ti.c:511 * * The consumer only ever takes values in [0, ring_cnt): 2048 / 512 / 256. * A DMA'd producer/consumer value OUTSIDE that range (e.g. 0x8000, or a * high bit set by faulty firmware / DMA tampering) makes the equality * unsatisfiable -> the loop never exits. It runs inside the interrupt * handler AFTER host interrupts were masked (if_ti.c:1778 * CSR_WRITE_4(sc, TI_MB_HOSTINTR, 1)) while holding ifp->if_serializer: * hard livelock (soft lockup; the CPU never returns from the IRQ). * txeof additionally decrements sc->ti_txcnt (int, if_ti.c:1755) each * spin -> signed underflow once it exits (it cannot while spinning). * * This program simulates both the benign and the poisoned loop with a * bounded iteration budget and reports termination. It does not touch * the kernel: the real path needs a Tigon NIC writing an out-of-range * index word. */ #include <stdio.h> struct loop_spec { const char *name; unsigned ring_cnt; unsigned cons_start; unsigned long prod_raw; }; static void simulate(const struct loop_spec *s) { unsigned long cons = s->cons_start; unsigned long iters = 0, budget = 8UL * s->ring_cnt + 16; int done = 0; while (iters < budget) { if (cons == s->prod_raw) { done = 1; break; } cons = (cons + 1) % s->ring_cnt; /* TI_INC */ iters++; } printf("%-14s ring=%-4u prod_raw=0x%08lx -> %s after %lu iterations" " (consumer range [0,%u])\n", s->name, s->ring_cnt, s->prod_raw, done ? "TERMINATES" : "NEVER TERMINATES (equality unsatisfiable)", iters, s->ring_cnt - 1); } int main(void) { struct loop_spec cases[] = { /* benign: producer inside modular range */ { "rxeof/benign", 2048, 0, 37 }, { "txeof/benign", 512, 0, 511 }, { "events/benign", 256, 0, 3 }, /* poisoned: 32-bit DMA'd word outside consumer's range */ { "rxeof/poison", 2048, 0, 0x00008000UL }, { "rxeof/poison2", 2048, 0, 4096UL }, /* 2x ring size */ { "txeof/poison", 512, 0, 0x00010000UL }, { "events/poison", 256, 0, 0xFFFFFFFFUL }, }; unsigned i; printf("DF-4068 model: ti completion walks keyed on raw equality with " "NIC-DMA'd u32 index (if_ti.c:511,1629,1729)\n\n"); for (i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) simulate(&cases[i]); printf("\nInterrupt handler masks host interrupts before walking " "(if_ti.c:1778) and holds ifp->if_serializer: an unsatisfiable " "loop = hard CPU livelock.\n"); printf("\nRC_DF4068_MODEL=0\n"); return 0; } |