⬢ DragonFlyBSD Kernel Audit
DF-4874 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/nata/atapi-fd.c	2026-09-24 17:15:01.460690390 +0000
+++ b/sys/dev/disk/nata/atapi-fd.c	2026-09-24 17:15:14.072526409 +0000
@@ -134,23 +134,45 @@
 static int
 afd_detach(device_t dev)
 {   
+    struct ata_channel *ch = device_get_softc(device_get_parent(dev));
     struct afd_softc *fdp = device_get_ivars(dev);
 
     /* check that we have a valid device to detach */
     if (!device_get_ivars(dev))
         return ENXIO;
-    
+
+    /*
+     * Refuse to detach while any disk/slice device is open: the cdevs
+     * outlive disk_destroy() and keep walking the embedded struct disk
+     * until their last close.  (DF-4874, twin of DF-4797)
+     */
+    if (fdp->disk.d_opencount != 0)
+	return EBUSY;
+
+    /*
+     * Stop the data path: no new bio may pass afd_strategy(), and the
+     * softc must survive until the last in-flight request's afd_done().
+     */
+    lockmgr(&ch->queue_mtx, LK_EXCLUSIVE);
+    fdp->flags |= AFD_F_DETACHING;
+    device_set_ivars(dev, NULL);
+    lockmgr(&ch->queue_mtx, LK_RELEASE);
+    while (fdp->refs)
+	tsleep(fdp, 0, "afdetach", hz);
+
+    /*
+     * Fail anything still queued or running on the channel so their
+     * biodone()s happen while the disk layer structures are intact.
+     */
+    ata_fail_requests(dev);
+
     /* detroy disk from the system so we dont get any further requests */
     disk_invalidate(&fdp->disk);
     disk_destroy(&fdp->disk);
 
-    /* fail requests on the queue and any thats "in flight" for this device */
-    ata_fail_requests(dev);
-
     /* dont leave anything behind */
     /* disk_destroy() already took care of the dev_ops */
     devstat_remove_entry(&fdp->stats);
-    device_set_ivars(dev, NULL);
     kfree(fdp, M_AFD);
     return 0;
 }
@@ -255,6 +277,25 @@
 	return 0;
     }
 
+    /*
+     * Serialize against afd_detach(): either we obtain a reference and
+     * the softc stays alive until our afd_done() drops it, or the driver
+     * is detaching and the bio is failed immediately.  (DF-4874)
+     */
+    {
+	struct ata_channel *ch = device_get_softc(device_get_parent(dev));
+
+	lockmgr(&ch->queue_mtx, LK_EXCLUSIVE);
+	fdp = device_get_ivars(dev);
+	if (fdp == NULL || (fdp->flags & AFD_F_DETACHING)) {
+	    lockmgr(&ch->queue_mtx, LK_RELEASE);
+	    biofinish(bp, NULL, ENXIO);
+	    return 0;
+	}
+	atomic_add_int(&fdp->refs, 1);
+	lockmgr(&ch->queue_mtx, LK_RELEASE);
+    }
+
     lba = bp->bio_offset / fdp->sectorsize;
     count = bbp->b_bcount / fdp->sectorsize;
     bbp->b_resid = bbp->b_bcount; 
@@ -326,7 +367,11 @@
     if ((bbp->b_error = request->result))
 	bbp->b_flags |= B_ERROR;
     bbp->b_resid = bbp->b_bcount - request->donecount;
-    devstat_end_transaction_buf(&fdp->stats, bbp);
+    if (fdp) {
+	devstat_end_transaction_buf(&fdp->stats, bbp);
+	atomic_subtract_int(&fdp->refs, 1);
+	wakeup(fdp);
+    }
     biodone(bp);
     ata_free_request(request);
 }
--- a/sys/dev/disk/nata/atapi-fd.h	2026-09-24 17:15:01.464690338 +0000
+++ b/sys/dev/disk/nata/atapi-fd.h	2026-09-24 17:15:14.060526565 +0000
@@ -80,7 +80,11 @@
     u_int32_t	blocksize;
 };
 
+#define AFD_F_DETACHING	0x0001
+
 struct afd_softc {
+    int			flags;
+    int			refs;		/* in-flight afd_strategy references */
     u_int64_t		mediasize;
     u_int32_t		heads;
     u_int32_t		sectors;