DragonFlyBSD Kernel Audit
DF-0533 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_var.h b/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_var.h
--- a/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_var.h
+++ b/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_var.h
@@ -92,6 +92,15 @@
 	u_int32_t		 status;	/* from ISR */
 	void			*ith;		/* ithread handler */
 
+	/*
+	 * Serializes inq/outq mutation between the hard ISR (bt3c_intr ->
+	 * bt3c_receive: IF_ENQUEUE) and the SWI/netgraph paths (bt3c_forward,
+	 * bt3c_send, bt3c_rcvdata: IF_DEQUEUE/IF_ENQUEUE). IF_*QUEUE macros
+	 * are lock-free linked-list ops, so without this the hard ISR can
+	 * corrupt the list mid-IF_DEQUEUE. (DF-0533)
+	 */
+	struct spinlock		 sc_lock;
+
 	struct mbuf		*m;		/* current frame */
 	u_int32_t		 want;		/* # of chars we want */
 
 diff --git a/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c b/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c
--- a/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c
+++ b/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c
@@ -53,6 +53,7 @@
 #include <sys/module.h>
 
 #include <sys/rman.h>
+#include <sys/spinlock.h>
 
 #include <sys/socket.h>
 #include <net/if.h>
@@ -554,6 +555,7 @@
 
 	NGI_GET_M(item, m);
 
+	spin_lock(&sc->sc_lock);
 	if (IF_QFULL(&sc->outq)) {
 		NG_BT3C_ERR(sc->dev,
 "Outgoing queue is full. Dropping mbuf, len=%d\n", m->m_pkthdr.len);
@@ -564,6 +566,7 @@
 		NG_FREE_M(m);
 	} else 
 		IF_ENQUEUE(&sc->outq, m);
+	spin_unlock(&sc->sc_lock);
 
 	error = ng_send_fn(sc->node, NULL, bt3c_send, NULL, 0 /* new send */);
 out:
@@ -666,6 +669,7 @@
 	sc->debug = NG_BT3C_WARN_LEVEL;
 
 	sc->inq.ifq_maxlen = sc->outq.ifq_maxlen = BT3C_DEFAULTQLEN;
+	spin_init(&sc->sc_lock, "bt3c sc");
 
 	sc->state = NG_BT3C_W4_PKT_IND;
 	sc->want = 1;
@@ -927,6 +931,7 @@
 			NG_BT3C_STAT_BYTES_RECV(sc->stat, sc->m->m_pkthdr.len);
 			NG_BT3C_STAT_PCKTS_RECV(sc->stat);
 
+			spin_lock(&sc->sc_lock);
 			if (IF_QFULL(&sc->inq)) {
 				NG_BT3C_ERR(sc->dev,
 "Incoming queue is full. Dropping mbuf, len=%d\n", sc->m->m_pkthdr.len);
@@ -939,6 +944,7 @@
 				IF_ENQUEUE(&sc->inq, sc->m);
 				sc->m = NULL;
 			}
+			spin_unlock(&sc->sc_lock);
 
 			sc->state = NG_BT3C_W4_PKT_IND;
 			sc->want = 1;
@@ -1013,6 +1019,7 @@
 		return;
 
 	if (sc->hook != NULL && NG_HOOK_IS_VALID(sc->hook)) {
+		spin_lock(&sc->sc_lock);
 		for (;;) {
 			IF_DEQUEUE(&sc->inq, m);
 			if (m == NULL)
@@ -1022,7 +1029,9 @@
 			if (error != 0)
 				NG_BT3C_STAT_IERROR(sc->stat);
 		}
+		spin_unlock(&sc->sc_lock);
 	} else {
+		spin_lock(&sc->sc_lock);
 		for (;;) {
 			IF_DEQUEUE(&sc->inq, m);
 			if (m == NULL)
@@ -1031,6 +1040,7 @@
 			NG_BT3C_STAT_IERROR(sc->stat);
 			NG_FREE_M(m);
 		}
+		spin_unlock(&sc->sc_lock);
 	}
 } /* bt3c_forward */
 
@@ -1056,6 +1066,7 @@
 
 	bt3c_set_address(sc, 0x7080);
 
+	spin_lock(&sc->sc_lock);
 	for (wrote = 0; wrote < BT3C_FIFO_SIZE; ) {
 		IF_DEQUEUE(&sc->outq, m);
 		if (m == NULL)
@@ -1084,6 +1095,7 @@
 
 		NG_BT3C_STAT_PCKTS_SENT(sc->stat);
 	}
+	spin_unlock(&sc->sc_lock);
 
 	if (wrote > 0) {
 		NG_BT3C_INFO(sc->dev, "Wrote %d bytes\n", wrote);