⬢ DragonFlyBSD Kernel Audit
DF-4721 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/raid/vinum/vinumrequest.c
+++ b/sys/dev/raid/vinum/vinumrequest.c
@@ -393,20 +393,31 @@
      */
     crit_enter();
     for (rqg = rq->rqg; rqg != NULL;) {			    /* through the whole request chain */
+	struct rqgroup *nextrqg;				    /* group snapshot */
+	int lastrqno;					    /* last rqe we will launch */
+
 	if (rqg->lockbase >= 0)				    /* this rqg needs a lock first */
 	    rqg->lock = lockrange(rqg->lockbase, rqg->rq->bio->bio_buf, &PLEX[rqg->plexno]);
 	rcount = rqg->count;
-	for (rqno = 0; rqno < rcount;) {
+	/*
+	 * Snapshot the group chain and find the last request we will
+	 * launch BEFORE firing anything: with drives that complete
+	 * inline (e.g. md malloc disks), the final completion can run
+	 * complete_rqe() -> complete_raid5_write() -> freerq() inside
+	 * dev_dstrategy(), freeing the whole request while we are
+	 * still in this loop.  We must not touch the group after the
+	 * last launch - neither trailing XFR_BAD_SUBDISK entries nor
+	 * rqg->next.
+	 */
+	nextrqg = rqg->next;
+	for (lastrqno = rcount - 1; lastrqno >= 0; lastrqno--)
+	    if ((rqg->rqe[lastrqno].flags & XFR_BAD_SUBDISK) == 0)
+		break;
+	for (rqno = 0; rqno <= lastrqno;) {
 	    cdev_t dev;
 
 	    rqe = &rqg->rqe[rqno];
-
-	    /*
-	     * Point to next rqg before the bottom end
-	     * changes the structures.
-	     */
-	    if (++rqno >= rcount)
-		rqg = rqg->next;
+	    rqno++;
 	    if ((rqe->flags & XFR_BAD_SUBDISK) == 0) {	    /* this subdisk is good, */
 		drive = &DRIVE[rqe->driveno];		    /* look at drive */
 		drive->active++;
@@ -436,6 +447,7 @@
 		dev_dstrategy(dev, &rqe->b.b_bio1);
 	    }
 	}
+	rqg = nextrqg;
     }
     crit_exit();
     return 0;