diff --git a/sys/bus/cam/scsi/scsi_sg.c b/sys/bus/cam/scsi/scsi_sg.c --- a/sys/bus/cam/scsi/scsi_sg.c +++ b/sys/bus/cam/scsi/scsi_sg.c @@ -390,6 +390,18 @@ return (ENXIO); /* + * Take a reference for the duration of the open, matching the + * cam_periph_release() that sgclose() issues unconditionally on + * every close. Without this, every open/close pair nets one + * decrement of the periph refcount and the periph can be freed + * out from under a live si_drv1 (UAF). + */ + if (cam_periph_acquire(periph) != CAM_REQ_CMP) + return (ENXIO); + + cam_periph_lock(periph); + + /* * Don't allow access when we're running at a high securelevel. */ if (securelevel > 1) { @@ -397,22 +409,18 @@ cam_periph_release(periph); return(EPERM); } - cam_periph_lock(periph); softc = (struct sg_softc *)periph->softc; if (softc->flags & SG_FLAG_INVALID) { cam_periph_unlock(periph); + cam_periph_release(periph); return (ENXIO); } - if ((softc->flags & SG_FLAG_OPEN) == 0) { + if ((softc->flags & SG_FLAG_OPEN) == 0) softc->flags |= SG_FLAG_OPEN; - cam_periph_unlock(periph); - } else { - /* Device closes aren't symmetrical, fix up the refcount. */ - cam_periph_unlock(periph); - cam_periph_release(periph); - } + + cam_periph_unlock(periph); return (error); }