DragonFlyBSD Kernel Audit
DF-2282 / fix.diff
← back to finding ↓ download raw
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);
 }