⬢ DragonFlyBSD Kernel Audit
DF-3593 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/netif/bwn/bwn/if_bwn.c
+++ b/sys/dev/netif/bwn/bwn/if_bwn.c
@@ -5982,6 +5982,25 @@ bwn_dma_handle_txeof(struct bwn_mac *mac,
 	dr = bwn_dma_parse_cookie(mac, status, status->cookie, &slot);
 	if (dr == NULL) {
 		device_printf(sc->sc_dev, "failed to parse cookie\n");
 		return;
 	}
+
+	/*
+	 * A duplicate TX-done for an already completed frame delivers a
+	 * valid cookie whose BODY descriptor was already cleared
+	 * (mt_m == NULL).  The firmware is known to emit duplicate TX DONE
+	 * events (see the XXX FIXME in bwn_dma_parse_cookie); completing a
+	 * frame twice NULL-derefs mt_ni and underflows dr_usedslot.
+	 * Peek at the BODY slot (HEADER slots never carry mt_m) and drop
+	 * the status instead.
+	 */
+	dr->getdesc(dr, slot, &desc, &meta);
+	if (!meta->mt_islast)
+		dr->getdesc(dr, bwn_dma_nextslot(dr, slot), &desc, &meta);
+	if (meta->mt_m == NULL) {
+		device_printf(sc->sc_dev,
+		    "%s: duplicate TX status (cookie %#x) ignored\n",
+		    __func__, status->cookie);
+		return;
+	}
+
 	KASSERT(dr->dr_tx, ("%s:%d: fail", __func__, __LINE__));
 
 	while (1) {
@@ -6048,6 +6067,16 @@ bwn_pio_handle_txeof(struct bwn_mac *mac,
 	tq = bwn_pio_parse_cookie(mac, status->cookie, &tp);
 	if (tq == NULL)
 		return;
+
+	/* Duplicate TX-done: packet already completed and free-listed. */
+	if (tp->tp_m == NULL) {
+		device_printf(sc->sc_dev,
+		    "%s: duplicate TX status (cookie %#x) ignored\n",
+		    __func__, status->cookie);
+		return;
+	}
 
 	tq->tq_used -= roundup(tp->tp_m->m_pkthdr.len + BWN_HDRSIZE(mac), 4);
 	tq->tq_free++;