DF-3989 / trigger.c
/* * DF-3989 trigger.c โ arithmetic model of ale_rxeof trusting the * controller-DMA'd 14-bit RRS frame length (DF-3604 / DF-3759 / DF-3933 * family, ale(4) instance). * * HARDWARE PRECONDITION (absent on audit guest โ virtio-net only, no * 1969:1026 AR8121/8113/8114 PCI device): a faulty or malicious Atheros * L1E controller writes an rx_rs into the DMA'd Rx page whose * length field (ALE_RD_LEN_MASK 0x3FFF0000, 14 bits โ if_alereg.h:675) * disagrees with the frame actually written, with none of the * ALE_RD_CRC|CODE|DRIBBLE|RUNT|OFLOW|TRUNC error bits set. * * Path (sys/dev/netif/ale/if_ale.c): * ale_intr :1981 -> ale_rxeof :2153 * :2204 length = ALE_RX_BYTES(le32toh(rs->length)); // 0..16383, no * // bound vs page * :2232 m = m_devget(rs + 1, length - ETHER_CRC_LEN, 0, ifp); * // bcopy of (length-4) bytes from * // rx_page->page_addr + rx_page->cons + sizeof(rx_rs) * // into a fresh mbuf -> delivered to the network stack * Page geometry (if_alevar.h / if_ale.c:940): * ALE_RX_PAGE_SZ = roundup(1522,32)*128 = 196608 * non-jumbo page = roundup(1522+196608,32) = 198144 * jumbo page (8121) = roundup(8132+196608,32) = 204768 * cons is a multiple of 32 bounded only by ">= ALE_RX_PAGE_SZ -> reset" * (ale_rx_update_page :2068-2075), so the last in-page rs sits at * cons = 196576; the copy then reads up to 196576+8+16379 = 212963. * * Result: heap OOB READ past the bus_dmamem_alloc'd page into adjacent * kernel memory, copied into an mbuf and passed up (bpf/socket payload = * kernel heap disclosure). Unlike bce (DF-3604)/alc (DF-3759), the * length<4 underflow is SAFE here: length-ETHER_CRC_LEN is uint32, passed * to int tot in m_devget -> <=0 loop never runs -> NULL -> drop * (sys/kern/uipc_mbuf.c:2235). */ #include <stdio.h> #include <stdint.h> #define ALE_RX_PAGE_SZ 196608u #define GUARD_NONJUMBO 1522u /* ALE_MAX_FRAMELEN */ #define GUARD_JUMBO 8132u /* ALE_JUMBO_FRAMELEN (AR8121) */ #define RX_PAGE_ALIGN 32u #define RS_SZ 8u /* sizeof(struct rx_rs) __packed */ #define ETHER_CRC_LEN 4u #define LEN_MASK_MAX 0x3FFFu /* 14-bit ALE_RD_LEN_MASK */ static unsigned roundup32(unsigned v) { return (v + 31u) & ~31u; } static void model(unsigned guard, const char *tag) { unsigned pagesize = roundup32(guard + ALE_RX_PAGE_SZ); unsigned cons_last = ALE_RX_PAGE_SZ - RX_PAGE_ALIGN; /* 196576 */ unsigned copy_len = LEN_MASK_MAX - ETHER_CRC_LEN; /* 16379 */ unsigned read_end = cons_last + RS_SZ + copy_len; printf("%s page = roundup(%u+%u,32) = %u bytes\n", tag, guard, ALE_RX_PAGE_SZ, pagesize); printf(" worst-case rs at cons=%u, copy %u bytes -> read ends %u\n", cons_last, copy_len, read_end); if (read_end > pagesize) printf(" => OOB READ past DMA page: %u bytes of adjacent " "kernel heap into mbuf -> if_input (kernleak)\n", read_end - pagesize); else printf(" => in-bounds\n"); } int main(void) { printf("DF-3989 model: ale_rxeof unbounded 14-bit RRS length\n" " (sys/dev/netif/ale/if_ale.c:2204,2232; if_alereg.h:675)\n\n"); model(GUARD_NONJUMBO, "non-jumbo (AR8113/AR8114)"); model(GUARD_JUMBO, "jumbo (AR8121)"); printf("\nlength<4 variant: m_devget(int tot <= 0) returns NULL " "-> iqdrops (SAFE, unlike bce/alc)\n"); printf("legitimate max frame (jumbo): %u (mtu %u + 18 hdr/vlan + 4 " "crc) < 16383 mask max -> any length in (%u..16383] is a " "device lie\n", 8110u + 18u + 4u, 8110u, 8110u + 22u); printf("\nTRIGGER_RC=0 (model OK; hardware-gated on this guest: " "no Atheros NIC)\n"); return (0); } |