⬢ DragonFlyBSD Kernel Audit
DF-4534 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/misc/lpt/lpt.c
+++ b/sys/dev/misc/lpt/lpt.c
@@ -595,14 +595,28 @@
 	}
 	callout_stop(&sc->sc_callout);
 	ppb_wctr(ppbus, LPC_NINIT);
-	kfree(sc->sc_inbuf, M_LPT);
-	kfree(sc->sc_statbuf, M_LPT);
+	/*
+	 * NB: the lptout callout (which calls lptintr -> lpt_intr without
+	 * the mplock) may still be executing on another cpu; callout_stop()
+	 * does not wait for it.  Defer the frees until after the bus (and
+	 * thus the interrupt handler, torn down by ppb_release_bus) is
+	 * released and the callout has been drained.
+	 */
+	callout_drain(&sc->sc_callout);
 
 end_close:
 	/* release the bus anyway
 	 * unregistration of interrupt forced by release
 	 */
 	lpt_release_ppbus(lptdev);
+	if (sc->sc_inbuf != NULL) {
+		kfree(sc->sc_inbuf, M_LPT);
+		sc->sc_inbuf = NULL;
+	}
+	if (sc->sc_statbuf != NULL) {
+		kfree(sc->sc_statbuf, M_LPT);
+		sc->sc_statbuf = NULL;
+	}
 
 	sc->sc_state = 0;
 	sc->sc_xfercnt = 0;