DragonFlyBSD Kernel Audit
DF-0981 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/bus/u4b/wlan/if_run.c b/sys/bus/u4b/wlan/if_run.c
--- a/sys/bus/u4b/wlan/if_run.c
+++ b/sys/bus/u4b/wlan/if_run.c
@@ -2998,6 +2998,25 @@
 			dmalen + 8, xferlen);
 			break;
 		}
+		/*
+		 * The per-frame DMA length is device-controlled and is only bounded
+		 * above by the bulk RX URB length (RUN_MAX_RXSZ == MJUMPAGESIZE ==
+		 * 4096).  When aggregated frames are copied out below, the destination
+		 * is an mbuf cluster; reject any frame whose DMA region plus the
+		 * trailing rt2870_rxd would not fit in a cluster (MCLBYTES == 2048),
+		 * so a malicious/buggy device cannot drive an out-of-bounds heap write
+		 * via m_copydata().
+		 */
+		if (dmalen + sizeof(struct rt2870_rxd) > MCLBYTES) {
+			DPRINTF("oversized DMA length %u > %d\n",
+			    dmalen + (int)sizeof(struct rt2870_rxd), MCLBYTES);
+#if defined(__DragonFly__)
+			++ic->ic_ierrors;
+#else
+			counter_u64_add(ic->ic_ierrors, 1);
+#endif
+			break;
+		}
 
 		/* If it is the last one or a single frame, we won't copy. */
 		if ((xferlen -= dmalen + 8) <= 8) {
@@ -3009,8 +3028,14 @@
 			break;
 		}
 
-		/* copy aggregated frames to another mbuf */
-		m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+		/*
+		 * Copy aggregated frames to another mbuf.  The per-frame DMA length
+		 * can be up to RUN_MAX_RXSZ, so the destination cluster must be at
+		 * least MJUMPAGESIZE bytes (matching sc->rx_m above); a plain
+		 * m_getcl() cluster is only MCLBYTES (2048) and would be overflowed
+		 * by m_copydata() for any dmalen > ~2044.
+		 */
+		m0 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE);
 		if (__predict_false(m0 == NULL)) {
 			DPRINTF("could not allocate mbuf\n");
 #if defined(__DragonFly__)