⬢ DragonFlyBSD Kernel Audit
DF-4783 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/mmcsd/mmcsd.c
+++ b/sys/dev/disk/mmcsd/mmcsd.c
@@ -86,6 +86,8 @@
 	daddr_t eblock, eend;	/* Range remaining after the last erase. */
 	int running;
 	int suspend;
+	int gone;		/* teardown excludes the data path */
+	int strategy_refs;	/* in-flight strategy()/open() callers */
 };
 
 /* bus entry points */
@@ -215,6 +217,7 @@
 
 	MMCSD_LOCK(sc);
 	sc->suspend = 0;
+	sc->gone = 1;		/* no new strategy()/open() work */
 	if (sc->running > 0) {
 		/* kill thread */
 		sc->running = 0;
@@ -223,6 +226,12 @@
 		while (sc->running != -1)
 			lksleep(sc, &sc->sc_lock, 0, "detach", 0);
 	}
+	/*
+	 * Drain callers inside mmcsd_strategy()/mmcsd_open() so none is
+	 * parked on sc_lock when we lockuninit() and newbus frees the softc.
+	 */
+	while (sc->strategy_refs > 0)
+		lksleep(sc, &sc->sc_lock, 0, "dtstrd", 0);
 	MMCSD_UNLOCK(sc);
 
 	/*
@@ -230,13 +239,21 @@
 	 *
 	 * XXX: Return all queued I/O with ENXIO. Is this correct?
 	 */
+	MMCSD_LOCK(sc);
 	while ((q_bio = bioq_takefirst(&sc->bio_queue)) != NULL) {
+		MMCSD_UNLOCK(sc);
 		q_bp = q_bio->bio_buf;
 		q_bp->b_resid = q_bp->b_bcount;
 		q_bp->b_error = ENXIO;
 		q_bp->b_flags |= B_ERROR;
 		biodone(q_bio);
+		MMCSD_LOCK(sc);
 	}
+	MMCSD_UNLOCK(sc);
+
+	/* Detach the raw cdev from the softc about to be freed. */
+	if (sc->dev_t != NULL)
+		sc->dev_t->si_drv1 = NULL;
 
 	/* kill disk */
 	disk_destroy(&sc->disk);
@@ -287,11 +304,23 @@
 {
 	struct mmcsd_softc *sc;
 
+	int error;
+
 	sc = (struct mmcsd_softc *)ap->a_head.a_dev->si_drv1;
-	if ((ap->a_oflags & FWRITE) && mmc_get_read_only(sc->dev))
-		return (EACCES);
+	if (sc == NULL)
+		return (ENXIO);
+	MMCSD_LOCK(sc);
+	if (sc->gone) {
+		MMCSD_UNLOCK(sc);
+		return (ENXIO);
+	}
+	sc->strategy_refs++;
+	error = ((ap->a_oflags & FWRITE) && mmc_get_read_only(sc->dev))
+	    ? EACCES : 0;
+	sc->strategy_refs--;
+	MMCSD_UNLOCK(sc);
 
-	return (0);
+	return (error);
 }
 
 static int
@@ -308,12 +337,22 @@
 	struct buf *bp = bio->bio_buf;
 
 	sc = (struct mmcsd_softc *)ap->a_head.a_dev->si_drv1;
+	if (sc == NULL) {
+		bp->b_error = ENXIO;
+		bp->b_flags |= B_ERROR;
+		bp->b_resid = bp->b_bcount;
+		biodone(bio);
+		return (0);
+	}
 	MMCSD_LOCK(sc);
-	if (sc->running > 0 || sc->suspend > 0) {
+	sc->strategy_refs++;
+	if (!sc->gone && (sc->running > 0 || sc->suspend > 0)) {
 		bioqdisksort(&sc->bio_queue, bio);
+		sc->strategy_refs--;
 		MMCSD_UNLOCK(sc);
 		wakeup(sc);
 	} else {
+		sc->strategy_refs--;
 		MMCSD_UNLOCK(sc);
 		bp->b_error = ENXIO;
 		bp->b_flags |= B_ERROR;