DF-2534 / fix.diff
--- 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); } |