⬢ DragonFlyBSD Kernel Audit
DF-2284 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/cam/scsi/scsi_sg.c
+++ b/sys/bus/cam/scsi/scsi_sg.c
@@ -180,6 +180,27 @@
 	softc->flags |= SG_FLAG_INVALID;
 
 	/*
+	 * DF-2284: nothing else ever frees the queued rdwr entries (only a
+	 * matching sgread() dequeues them), so write-only usage leaks every
+	 * sg_rdwr + ccb + buffer.  Drop completed entries here; sgcleanup()
+	 * drains whatever remains before the softc is freed.
+	 */
+	{
+		struct sg_rdwr *rdwr, *rdwr2;
+
+		TAILQ_FOREACH_MUTABLE(rdwr, &softc->rdwr_done, rdwr_link,
+		    rdwr2) {
+			if (rdwr->state == SG_RDWR_DONE) {
+				TAILQ_REMOVE(&softc->rdwr_done, rdwr,
+				    rdwr_link);
+				xpt_free_ccb(&rdwr->ccb->ccb_h);
+				kfree(rdwr->buf, M_DEVBUF);
+				kfree(rdwr, M_DEVBUF);
+			}
+		}
+	}
+
+	/*
 	 * XXX Return all queued I/O with ENXIO.
 	 * XXX Handle any transactions queued to the card
 	 *     with XPT_ABORT_CCB.
@@ -202,6 +223,21 @@
 	cam_periph_unlock(periph);
 	destroy_dev(softc->dev);
 	cam_periph_lock(periph);
+	/*
+	 * DF-2284: final drain -- anything still on rdwr_done is
+	 * unreachable once the softc is freed.
+	 */
+	{
+		struct sg_rdwr *rdwr, *rdwr2;
+
+		TAILQ_FOREACH_MUTABLE(rdwr, &softc->rdwr_done, rdwr_link,
+		    rdwr2) {
+			TAILQ_REMOVE(&softc->rdwr_done, rdwr, rdwr_link);
+			xpt_free_ccb(&rdwr->ccb->ccb_h);
+			kfree(rdwr->buf, M_DEVBUF);
+			kfree(rdwr, M_DEVBUF);
+		}
+	}
 	kfree(softc, M_DEVBUF);
 }