⬢ DragonFlyBSD Kernel Audit
DF-4653 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/misc/puc/puc.c
+++ b/sys/dev/misc/puc/puc.c
@@ -88,6 +88,7 @@
 #include <sys/conf.h>
 #include <sys/malloc.h>
 #include <sys/rman.h>
+#include <cpu/cpufunc.h>	/* cpu_ccfence() */
 
 #include <bus/pci/pcireg.h>
 #include <bus/pci/pcivar.h>
@@ -405,6 +406,7 @@
 	ilr_mask = sc->ilr_enabled ? puc_ilr_read(sc) : 0xffffffff;
 	for (i = 0; i < PUC_MAX_PORTS; i++)
 		if (sc->sc_ports[i].ihand != NULL &&
+		    sc->sc_ports[i].ihandarg != NULL &&
 		    ((ilr_mask >> i) & 0x00000001))
 			(sc->sc_ports[i].ihand)(sc->sc_ports[i].ihandarg);
 }
@@ -688,8 +690,12 @@
 		if (sc->sc_ports[i].dev == child) {
 			if (sc->sc_ports[i].ihand != NULL)
 				return (ENXIO);
-			sc->sc_ports[i].ihand = ihand;
+			/* Publish the argument BEFORE the handler: puc_intr
+			 * gates on ihand alone, so ihandarg must become
+			 * visible no later than ihand. */
 			sc->sc_ports[i].ihandarg = arg;
+			cpu_ccfence();
+			sc->sc_ports[i].ihand = ihand;
 			KKASSERT(serializer == NULL); /* not handled yet XXX */
 			*cookiep = arg;
 			return (0);
@@ -708,7 +714,10 @@
 	sc = (struct puc_softc *)device_get_softc(dev);
 	for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) {
 		if (sc->sc_ports[i].dev == child) {
+			/* Retract the handler BEFORE the argument (inverse
+			 * of the setup publication order). */
 			sc->sc_ports[i].ihand = NULL;
+			cpu_ccfence();
 			sc->sc_ports[i].ihandarg = NULL;
 			return (0);
 		}