⬢ DragonFlyBSD Kernel Audit
DF-4497 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/netif/sbsh/if_sbsh.c
+++ b/sys/dev/netif/sbsh/if_sbsh.c
@@ -608,6 +608,10 @@ sbsh_watchdog(struct ifnet *ifp)
 static void
 resume_tx(struct sbsh_softc *sc)
 {
 	u_int32_t	cur_tbd = sc->regs->CTDR;
 
+	if (sc->state != ACTIVE)
+		return;
+
 	while (cur_tbd != sc->regs->LTDR
 		&& (sc->tbd[cur_tbd++].length & LAST_FRAG) == 0)
 		;
@@ -726,6 +730,10 @@ static void
 free_sent_buffers(struct sbsh_softc *sc)
 {
 	u_int32_t  cur_tbd;
 
+	if (sc->state != ACTIVE)
+		return;
+
 	cur_tbd = sc->regs->CTDR;
 
 	while (sc->head_tdesc != cur_tbd) {
@@ -790,6 +798,10 @@ static void
 indicate_frames(struct sbsh_softc *sc)
 {
 	struct ifnet *ifp = &sc->arpcom.ac_if;
 	unsigned  cur_rbd = sc->regs->CRDR & 0x7f;
 
+	if (sc->state != ACTIVE)
+		return;
+
 	while (sc->head_rdesc != cur_rbd) {
 		struct mbuf  *m = sc->rq[sc->head_rq++];
 		sc->head_rq &= (RQLEN - 1);
@@ -811,12 +823,16 @@ static void
 drop_queues(struct sbsh_softc *sc)
 {
 	while (sc->head_rq != sc->tail_rq) {
-		m_freem(sc->rq[sc->head_rq++]);
+		m_freem(sc->rq[sc->head_rq]);
+		sc->rq[sc->head_rq] = NULL;
+		sc->head_rq = (sc->head_rq + 1) & (RQLEN - 1);
 		sc->head_rq &= (RQLEN - 1);
 	}
 
 	while (sc->head_xq != sc->tail_xq) {
-		m_freem(sc->xq[sc->head_xq++]);
+		m_freem(sc->xq[sc->head_xq]);
+		sc->xq[sc->head_xq] = NULL;
+		sc->head_xq = (sc->head_xq + 1) & (XQLEN - 1);
 		sc->head_xq &= (XQLEN - 1);
 	}
 }
@@ -849,10 +865,14 @@ static void
 deactivate(struct sbsh_softc *sc)
 {
+	/* Drain any TXS/RXS/UFL already latched in SR: once the queues are
+	 * dropped below, free_sent_buffers()/indicate_frames()/resume_tx()
+	 * must not run against stale xq/rq pointers (they are also guarded
+	 * by sc->state, this covers a deferred ISR racing this function). */
+	sc->regs->SR = TXS | RXS | UFL | CRC | OFL;
 	sc->regs->CR &= ~(RXEN | TXEN);
 	sc->regs->CRB |= RXDE;
 	sc->regs->IMR  = EXT;
 	sc->regs->CTDR = sc->regs->LTDR;
 	sc->regs->CRDR = sc->regs->LRDR;
+	sc->head_tdesc = sc->regs->CTDR & 0x7f;
+	sc->head_rdesc = sc->regs->CRDR & 0x7f;
 	sc->state = ACTIVATION;
 
 	drop_queues(sc);