# DF-3716 — mxge_rx_done_big trusts the NIC-DMA'd 16-bit completion length with no posted-cluster bound ## Vulnerability `sys/dev/netif/mxge/if_mxge.c` RX completion path: - `mxge_clean_rx_done()` (:2337-2362) drains the NIC-DMA'd `rx_done->entry[]` slots. Each `mcp_slot_t` (mxge_mcp.h:56-60) carries a **16-bit big-endian `length` written by the NIC via DMA** (:2345). - Dispatch (:2350-2356): `length <= MXGE_RX_SMALL_BUFLEN` (= MHLEN-2, :91) goes to `mxge_rx_done_small()` (bounded by construction); **everything else — up to 65535 — goes to `mxge_rx_done_big()`** with no bound at all against the posted big-cluster size. - `mxge_rx_done_big()` (:2223-2278) sets `m->m_len = m->m_pkthdr.len = len` (:2261) on an mbuf whose cluster is `rx->cl_size` bytes — `MCLBYTES` (2048) for MTU ≤ 2027, `MJUMPAGESIZE` (4096) for MTU up to the driver max 4075 (`mxge_choose_params` :3228-3239, `max_mtu` :4429-4430) — and hands it to `ifp->if_input` (:2277). - Downstream (`mxge_rx_csum` :2150-2176 reads the IP header at eh+1, `mxge_vlan_tag_remove` :2178-2220 reads 4 bytes at offset 14, then the protocol stack copies `m_len` bytes toward sockets), the kernel reads up to `65535 - (cl_size - MXGEFW_PAD)` bytes **past the end of the 2/4 KiB cluster** — a heap OOB read / kernel-info-leak primitive of the same shape and size class as DF-3604 (bce), DF-3639 (bnx), DF-3663 (bge), DF-3679 (igb) and DF-3713 (em). Contrast: the small-buffer path *is* bounded (the `<= MXGE_RX_SMALL_BUFLEN` test at :2350), which makes the missing big-path bound an oversight, not a design choice. Also note `mxge_get_buf_big` (:2089-2140) posts exactly `cl_size` bytes (`m_getcl`/`m_getjcl`), so the posted-buffer size is driver-known and the fix is a one-line comparison. ## Threat model Attacker-influenced input = NIC-DMA'd completion slot (malicious/compromised PCIe device, or a device firmware bug / SRAM corruption producing a bogus length). Same trust boundary the family findings were filed under; NOT triggerable by network packets alone on a well-behaved NIC (the firmware clips to the negotiated MTU and posted buffer size). ## PoC Hardware-gated: the audit guest has no Myricom NIC (no 0x14c1:0x0008/0009 PCI device; `if_mxge.ko` present but unloaded). Per family convention (DF-3604/3639/3663/3679/3713 all `inconclusive`), this is source-certain but not runtime-testable here. `fix.diff` clamps the length against the posted cluster (`len > rx->cl_size - MXGEFW_PAD` → drop, ierrors++). ## Fix ```diff --- a/sys/dev/netif/mxge/if_mxge.c +++ b/sys/dev/netif/mxge/if_mxge.c @@ mxge_rx_done_big() idx = rx->cnt & rx->mask; rx->cnt++; + /* + * The length is NIC-supplied DMA data; never let it describe + * more bytes than the posted cluster actually holds. + */ + if (__predict_false(len > (uint32_t)rx->cl_size - MXGEFW_PAD)) { + IFNET_STAT_INC(ifp, ierrors, 1); + return; + } + /* Save a pointer to the received mbuf */ m = rx->info[idx].m; ``` (full git-apply-clean diff in fix.diff)