⬢ DragonFlyBSD Kernel Audit
DF-2534 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/disk/xdisk/xdisk.c	2026-10-19 20:05:30.113085120 +0000
+++ b/sys/dev/disk/xdisk/xdisk.c	2026-10-19 20:05:30.133084863 +0000
@@ -798,10 +798,27 @@
 static int
 xa_strategy(struct dev_strategy_args *ap)
 {
-	xa_softc_t *sc = ap->a_head.a_dev->si_drv1;
+	xa_softc_t *sc;
 	xa_tag_t *tag;
 	struct bio *bio = ap->a_bio;
+	struct buf *bp = bio->bio_buf;
 
+	/*
+	 * Lifetime interlock: xa_terminate_check() NULLs si_drv1 and frees
+	 * the softc only under xdisk_lk, and only when bioq/tag_pendq are
+	 * empty.  Check si_drv1 under xdisk_lk and queue the bio before
+	 * releasing it so the softc cannot be freed out from under us.
+	 * (Lock order xdisk_lk -> sc->lk matches xaio_rcvdmsg().)
+	 */
+	lockmgr(&xdisk_lk, LK_EXCLUSIVE);
+	sc = ap->a_head.a_dev->si_drv1;
+	if (sc == NULL || sc->terminating) {
+		lockmgr(&xdisk_lk, LK_RELEASE);
+		bp->b_error = ENXIO;
+		bp->b_flags |= B_ERROR;
+		biodone(bio);
+		return(0);
+	}
 	devstat_start_transaction(&sc->stats);
 	atomic_add_int(&xa_active, 1);
 	xa_last = bio->bio_offset;
@@ -815,6 +832,7 @@
 	if (tag)
 		xa_start(tag, NULL, 1);
 	lockmgr(&sc->lk, LK_RELEASE);
+	lockmgr(&xdisk_lk, LK_RELEASE);
 
 	return(0);
 }
@@ -830,9 +848,17 @@
 {
 	struct xa_softc *sc;
 
-	if ((sc = ap->a_head.a_dev->si_drv1) == NULL)
+	/*
+	 * xa_terminate_check() NULLs si_drv1 and frees the softc under
+	 * xdisk_lk; read and dereference under the same lock.
+	 */
+	lockmgr(&xdisk_lk, LK_EXCLUSIVE);
+	if ((sc = ap->a_head.a_dev->si_drv1) == NULL) {
+		lockmgr(&xdisk_lk, LK_RELEASE);
 		return (ENXIO);
+	}
 	ap->a_result = sc->info.d_media_blocks;
+	lockmgr(&xdisk_lk, LK_RELEASE);
 	return (0);
 }