DF-3663 / trigger.c
/* * DF-3663 โ bge(4) RX path trusts NIC-DMA'd return-ring bge_len * * Trigger (HARDWARE REQUIRED โ cannot run on the audit guest): * * The BCM57xx (bge) RX completion path processes `struct bge_rx_bd` * entries in the host-memory RX return ring that the NIC fills in via * DMA. sys/dev/netif/bge/if_bge.c: * * 3058: cur_rx = &sc->bge_ldata.bge_rx_return_ring * [sc->bge_rx_saved_considx]; * 3061: rxidx = cur_rx->bge_idx; <- NIC-written (DF-1159) * 3110: m = sc->bge_cdata.bge_rx_std_chain[rxidx].bge_mbuf; * 3117: if (bge_newbuf_std(sc, sc->bge_std, 0)) ... <- posts a fresh * 2048-byte cluster * 3144: m->m_pkthdr.len = m->m_len = cur_rx->bge_len - ETHER_CRC_LEN; * ^^^^^^ NIC-written uint16_t * (if_bgereg.h:2330), * NO MCLBYTES/jumbo bound * * The receiving buffers are: * - std ring: m_getcl() cluster, MCLBYTES == 2048, m_adj(ETHER_ALIGN) * => 2046 bytes usable (if_bge.c:1024-1030,1027); * - jumbo ring (bge HAS jumbo support, unlike the bnx sibling * DF-3639): driver-internal pool slot of BGE_JUMBO_FRAMELEN == 9018 * bytes, m_adj(ETHER_ALIGN) => 9016 usable (if_bge.c:1087-1114, * if_bgevar.h:56). The 384 slots are carved from one contiguous * BGE_JMEM DMA block (if_bge.c:910-921), so overflow of the LAST * slot runs past the entire allocation. * * A faulty or malicious NIC (firmware compromise, PCIe DMA attack, * erratum) that writes bge_len > 2050 (std) / > 9020 (jumbo) into the * return-ring BD makes the driver hand the network stack an mbuf * describing up to 65531 bytes inside the small buffer: * * - ether_input (sys/net/if_ethersubr.c:1512) runs ETHER_BPF_MTAP() * on it => the bpf(4) tap copies up to ~63.5 KB of kernel heap * PAST the cluster out to the capturing socket (kernel heap info * disclosure); * - IP-stack header validation and socket copies read the same OOB * range (further leak / fault when the walk crosses an unmapped * page => panic); * - on !x86_64 builds ONLY (i386), the RX_ALIGNBUG path first does * bcopy(m->m_data, m->m_data + ETHER_ALIGN, cur_rx->bge_len) * (if_bge.c:3138-3141) with the same untrusted length => kernel * heap OOB *WRITE* (CWE-787). BCM5701 PCI-X sets * BGE_FLAG_RX_ALIGNBUG (if_bge.c:2206-2208). The audit guest and * all x86_64 kernels compile this out via #if !defined(__x86_64__). * * bge_len < ETHER_HDR_LEN + ETHER_CRC_LEN (18) yields a negative * m_len which DragonFly's ether_input discards at * sys/net/if_ethersubr.c:1502-1506 (`m_len < sizeof(ether_header)') * => benign drop (no underflow panic on this path โ unlike the bce * sibling DF-3604, where the length field is unsigned in m_len math). * * Hardware-layer bounds that a *healthy* NIC enforces (and which this * bug removes as the last line of defense): * - std producer BD len = m->m_len = 2046/9016 (if_bge.c:1070,1136), * - MAC max RX frame BGE_RX_MTU = mtu+14+4+4 (if_bge.c:3769-3770), * - std RCB max frame 1536 for pre-5705 (if_bge.c:1575-1576). * * On real hardware the reproduction is: * 1. Attach a BCM570x/57xx based NIC (bge0). * 2. Have the device DMA a return-ring BD with bge_len = 65535 and * clear error flags (modified firmware / malicious peripheral / * erratum). * 3. `tcpdump -i bge0 -w out.pcap` (or any bpf listener) captures * up to 65531 bytes per frame => adjacent kernel heap disclosed. * * The audit QEMU guest has virtio-net only (run.log: NO_BROADCOM_ * DEVICE); bge is compiled into the kernel (kldstat -v: pci/if_bge) * but bge_attach/bge_rxeof are unreachable there, so this trigger * CANNOT be exercised on it. See VERDICT.md for the source-level * proof and the guest evidence. */ #include <sys/types.h> #include <stdio.h> int main(void) { fprintf(stderr, "DF-3663: this trigger requires bge(4) hardware " "(BCM5700-5723/575x-590x).\n" "On such a system: configure the NIC, force the device to write\n" "bge_len > 2050 (std ring) or > 9020 (jumbo ring) into an RX\n" "return-ring BD, then capture the handed-up frame\n" "(tcpdump -i bge0 -w out.pcap) and verify the captured frame\n" "length exceeds the posted buffer size (2046 std / 9016 jumbo).\n"); return 0; } |