⬢ DragonFlyBSD Kernel Audit
DF-5116 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/firewire/firewirereg.h
+++ b/sys/bus/firewire/firewirereg.h
@@ struct firewire_dev_comm {
 	struct firewire_comm *fc;
 	device_t dev;
 	void (*post_explore) (void *);
 	void (*post_busreset) (void *);
+	int	fd_cb_ref;	/* fw_busreset dispatch drain counter;
+				 * child detach must clear post_busreset and
+				 * wait for this to fall back to 0 */
 };
 
 struct fw_xferq {
--- a/sys/bus/firewire/firewire.c
+++ b/sys/bus/firewire/firewire.c
@@ fw_busreset(struct firewire_comm *fc)
 	if (device_get_children(fc->bdev, &devlistp, &devcnt) == 0) {
 		for( i = 0 ; i < devcnt ; i++)
 			if (device_get_state(devlistp[i]) >= DS_ATTACHED)  {
 				fdc = device_get_softc(devlistp[i]);
-				if (fdc->post_busreset != NULL)
-					fdc->post_busreset(fdc);
+				/*
+				 * Take the dispatch reference BEFORE the
+				 * NULL check so a concurrent detach that
+				 * clears post_busreset and drains fd_cb_ref
+				 * cannot free the softc underneath us.
+				 */
+				atomic_add_int(&fdc->fd_cb_ref, 1);
+				if (fdc->post_busreset != NULL)
+					fdc->post_busreset(fdc);
+				atomic_subtract_int(&fdc->fd_cb_ref, 1);
+				wakeup(&fdc->fd_cb_ref);
 			}
 		kfree(devlistp, M_TEMP);
 	}
--- a/sys/dev/misc/dcons/dcons_crom.c
+++ b/sys/dev/misc/dcons/dcons_crom.c
@@ dcons_crom_detach(device_t dev)
 	sc = (struct dcons_crom_softc *) device_get_softc(dev);
-	sc->fd.post_busreset = NULL;
+	/* detach the bus-reset callback and drain any in-flight
+	 * fw_busreset dispatch before returning: newbus frees the softc
+	 * right after this method returns. */
+	sc->fd.post_busreset = NULL;
+	while (sc->fd.fd_cb_ref)
+		tsleep(&sc->fd.fd_cb_ref, 0, "dcromdr", 1);
 
 	/* XXX */
 	if (dcons_conf->dma_tag == sc->dma_tag)