DragonFlyBSD Kernel Audit
DF-1157 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/netif/wpi/if_wpi.c b/sys/dev/netif/wpi/if_wpi.c
--- a/sys/dev/netif/wpi/if_wpi.c
+++ b/sys/dev/netif/wpi/if_wpi.c
@@ -1966,6 +1966,23 @@
 	bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);
 	head = (struct wpi_rx_head *)((caddr_t)(stat + 1) + stat->len);
 	len = le16toh(head->len);
+
+	/*
+	 * Upper-bound the firmware-reported frame length against the RX
+	 * cluster before computing/dereferencing tail.  `head` sits inside
+	 * data->m's MJUMPAGESIZE (4096) DMA buffer; without this check a
+	 * buggy or hostile firmware/PHY reporting len up to 65535 made
+	 * (head + 1) + len -- and thus tail -- point past the 4KB cluster,
+	 * dereferencing tail->flags (OOB heap read) and later setting
+	 * m_len = len for an oversized ieee80211_input walk.  Same class of
+	 * fix as iwn (DF-1123).
+	 */
+	if ((caddr_t)(head + 1) + len + sizeof(struct wpi_rx_tail) >
+	    mtod(data->m, caddr_t) + MJUMPAGESIZE) {
+		DPRINTF(sc, WPI_DEBUG_RECV, "%s: frame too long: %d\n",
+		    __func__, len);
+		goto fail1;
+	}
 	tail = (struct wpi_rx_tail *)((caddr_t)(head + 1) + len);
 	flags = le32toh(tail->flags);