# DF-4232 — txp(4) walks device-fed ring cursors unvalidated (OOB descriptor walk, wild `rx_sd` deref, non-terminating loops)

**File:** `sys/dev/netif/txp/if_txp.c` (pass 2)
**Severity:** High (memcorrupt bucket) — hardware-gated (3c990 Typhoon)
**Status:** source-certain; runtime inconclusive (no 3c990 on the guest)

## Build / run — same as DF-4231 (see that README); `t4232.c` prints the sink map.

## The bug (two sites, one systemic cause)

All ring cursors live in the DMA-shared hostvar block (`struct txp_hostvar`,
`if_txpreg.h:542-554`) or in the DMA-shared descriptor rings; the driver
consumes them with **no range or alignment validation**:

1. **`txp_rx_reclaim()` — `if_txp.c:658-737`.**
   `roff = *r->r_roff; woff = *r->r_woff;` (device-fed), then
   `rxd = r->r_desc + (roff / sizeof(struct txp_rx_desc))` and
   `while (roff != woff)` advancing `roff` by 16 with an exact-equality wrap
   at `RX_ENTRIES * 16` (2048). A `roff`/`woff` outside `[0,2048)` or not a
   multiple of 16 ⇒ (a) `rxd` starts/continues out of bounds past the
   `contigmalloc`'d `sc_ldata` block, (b) the loop can never terminate
   (`roff` cycles values never equal to `woff`) — unbounded walk under the
   ifnet serializer = livelock, and (c) `sd = rxd->rx_sd` (`if_txp.c:671`)
   reads a **kernel pointer stored in DMA memory** from OOB memory, then
   `m = sd->sd_mbuf` (`if_txp.c:673`) dereferences it and the wild `m` is fed
   to `m_copydata`/`m_freem`/`if_input` (`if_txp.c:704-728`).

2. **`txp_response()` — `if_txp.c:1418-1453`.**
   `while (ridx != hv->hv_resp_write_idx)` with firmware-fed `widx`; `ridx`
   only takes values {0,16,…,496}, so a `widx` that is not one of those
   makes the loop cycle forever (console spam + serializer-held livelock).
   Entry `ridx = hv->hv_resp_read_idx` (`if_txp.c:1393`) is also device-
   writable shared memory: `rsp = base + ridx` (`if_txp.c:1419`) is an OOB
   descriptor read, and `rsp->rsp_numdesc` from it sizes a `kmalloc`
   (`if_txp.c:1422`) and a bcopy walk (`txp_rsp_fixup`, `if_txp.c:1458-1481`).

Siblings already accepted by this audit: DF-3761 (unbounded-walk family).
Not covered by DF-1461 (that is the `txp_rxbuf_reclaim` kfree error path).

## Fix

`fix.diff`: validate/mask cursors to the ring byte size and 16-alignment
before walking; bail the descriptor instead of dereferencing garbage.
