DragonFlyBSD Kernel Audit
DF-1122 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/netif/iwn/if_iwn.c b/sys/dev/netif/iwn/if_iwn.c
--- a/sys/dev/netif/iwn/if_iwn.c
+++ b/sys/dev/netif/iwn/if_iwn.c
@@ -3264,8 +3264,30 @@
 	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
 
 	qid = le16toh(ba->qid);
-	txq = &sc->txq[ba->qid];
-	tap = sc->qid2tap[ba->qid];
+	/*
+	 * The firmware supplies the queue id in the Compressed BlockAck
+	 * notification.  Bounds-check it against sc->ntxqs (the size of both
+	 * sc->txq[] and sc->qid2tap[], which are IWN*_NTXQUEUES entries) and
+	 * use the byte-swapped `qid` consistently -- the old code indexed the
+	 * arrays with the RAW ba->qid and dereferenced tap without a NULL
+	 * check, so a notification with qid>=ntxqs was an OOB array read and a
+	 * notification for a TID whose aggregation was torn down (qid2tap[]
+	 * NULLed by iwn_ampdu_tx_stop) was a NULL-deref panic.
+	 */
+	if (qid >= sc->ntxqs) {
+		DPRINTF(sc, IWN_DEBUG_XMIT,
+		    "%s: compressed_ba qid %d out of range (ntxqs %d)\n",
+		    __func__, qid, sc->ntxqs);
+		return;
+	}
+	txq = &sc->txq[qid];
+	tap = sc->qid2tap[qid];
+	if (tap == NULL) {
+		DPRINTF(sc, IWN_DEBUG_XMIT,
+		    "%s: compressed_ba qid %d has no aggregate tap\n",
+		    __func__, qid);
+		return;
+	}
 	tid = tap->txa_tid;
 	wn = (void *)tap->txa_ni;