⬢ DragonFlyBSD Kernel Audit
DF-4341 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/netif/mn/if_mn.c
+++ b/sys/dev/netif/mn/if_mn.c
@@ -238,10 +238,11 @@
 enum framing {WHOKNOWS, E1, E1U, T1, T1U};
 
 struct softc {
-	int	unit;
+	int		unit;
 	device_t	dev;
 	struct resource *irq;
 	void *intrhand;
+	struct lwkt_serializer slicer;	/* serializes ISR vs netgraph methods */
 	enum framing	framing;
 	int 		nhooks;
 	void 		*m0v, *m1v;
@@ -467,15 +468,19 @@
 		chan = ffs(ts) - 1;
 	else
 		chan = 1;
+	lwkt_serialize_enter(&sc->slicer);
 	if (!sc->ch[chan])
 		mn_create_channel(sc, chan);
-	else if (sc->ch[chan]->state == UP)
+	else if (sc->ch[chan]->state == UP) {
+		lwkt_serialize_exit(&sc->slicer);
 		return (EBUSY);
+	}
 	sc->ch[chan]->ts = ts;
 	sc->ch[chan]->hook = hook;
 	sc->ch[chan]->tx_limit = nbit * 8;
 	hook->private = sc->ch[chan];
 	sc->nhooks++;
+	lwkt_serialize_exit(&sc->slicer);
 	return(0);
 }
 
@@ -579,16 +584,20 @@
 	struct softc *sc;
 	int chan, pitch, len;
 
+	/* mn_intr may be walking/freeing the very ring we append to */
 	sch = hook->private;
 	sc = sch->sc;
+	lwkt_serialize_enter(&sc->slicer);
 	chan = sch->chan;
 
 	if (sch->state != UP) {
 		NG_FREE_DATA(m, meta);
+		lwkt_serialize_exit(&sc->slicer);
 		return (0);
 	}
 	if (sch->tx_pending + m->m_pkthdr.len > sch->tx_limit * mn_maxlatency) {
 		NG_FREE_DATA(m, meta);
+		lwkt_serialize_exit(&sc->slicer);
 		return (0);
 	}
 	NG_FREE_META(meta);
@@ -641,6 +650,7 @@
 		sch->tx_pending += m->m_pkthdr.len;
 		sc->m32x->txpoll &= ~(1 << chan);
 	}
+	lwkt_serialize_exit(&sc->slicer);
 	return (0);
 }
 
@@ -657,12 +667,15 @@
 	struct schan *sch;
 	u_int32_t u;
 
+	/* serialize against mn_intr, which starts walking these rings as
+	 * soon as the channel comes up below */
 	sch = hook->private;
-	chan = sch->chan;
 	sc = sch->sc;
+	lwkt_serialize_enter(&sc->slicer);
+	chan = sch->chan;
 
 	if (sch->state == UP)
-		return (0);
+		goto out;
 	sch->state = UP;
 
 	/* Count and configure the timeslots for this channel */
@@ -734,6 +747,8 @@
 		kprintf("%s: init chan %d stat %08x\n", sc->name, chan, u);
 	sc->m32x->stat = 1;
 
+out:
+	lwkt_serialize_exit(&sc->slicer);
 	return (0);
 }
 
@@ -749,12 +764,16 @@
 	struct trxd *dp, *dp2;
 	u_int32_t u;
 
+	/* serialize against mn_intr / mn_rx_intr / mn_tx_intr */
 	sch = hook->private;
-	chan = sch->chan;
 	sc = sch->sc;
+	lwkt_serialize_enter(&sc->slicer);
+	chan = sch->chan;
 
-	if (sch->state == DOWN)
+	if (sch->state == DOWN) {
+		lwkt_serialize_exit(&sc->slicer);
 		return (0);
+	}
 	sch->state = DOWN;
 
 	/* Set receiver & transmitter off */
@@ -769,10 +788,24 @@
 	/* Initialize this channel */
 	sc->m32_mem.ccb = 0x00008000 + (chan << 8);
 	sc->m32x->cmd = 0x1;
-	DELAY(30);
-	u = sc->m32x->stat;
-	if (!(u & 1))
-		kprintf("%s: zap chan %d stat %08x\n", sc->name, chan, u);
+	/* Poll the channel-zap to completion; never free DMA targets under
+	 * a still-armed MUNICH32X (device write-after-free otherwise). */
+	for (i = 0; i < 100; i++) {
+		DELAY(30);
+		u = sc->m32x->stat;
+		if (u & 1)
+			break;
+	}
+	if (!(u & 1)) {
+		kprintf("%s: zap chan %d FAILED stat %08x, leaking rings\n",
+		    sc->name, chan, u);
+		/* Keep the descriptors + mbufs allocated: the device may
+		 * still own them.  Channel is DOWN so nothing else touches
+		 * them; this leaks a bounded number of descriptors only on
+		 * real hardware failure. */
+		lwkt_serialize_exit(&sc->slicer);
+		return (0);
+	}
 	sc->m32x->stat = 1;
 
 	/* Free all receive descriptors and mbufs */
@@ -792,6 +825,7 @@
 		sc->ch[chan]->x1 = dp2 = dp->vnext;
 		mn_free_desc(dp);
 	}
+	lwkt_serialize_exit(&sc->slicer);
 	sc->nhooks--;
 	return(0);
 }
@@ -1297,6 +1331,7 @@
 	sc->unit = device_get_unit(self);
 	sc->framing = E1;
 	ksprintf(sc->name, "mn%d", sc->unit);
+	lwkt_serialize_init(&sc->slicer);
 
         rid = PCIR_MAPS;
         res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
@@ -1327,7 +1362,7 @@
 	}
 
 	error = bus_setup_intr(self, sc->irq, INTR_MPSAFE, mn_intr, sc,
-			       &sc->intrhand, NULL);
+			       &sc->intrhand, &sc->slicer);
 
 	if (error) {
 		kprintf("couldn't set up irq\n");