⬢ DragonFlyBSD Kernel Audit
DF-5613 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/firewire/fwdev.c
+++ b/sys/bus/firewire/fwdev.c
@@ -145,6 +145,12 @@
 		q->buf = NULL;
 		kfree(q->bulkxfer, M_FW);
 		q->bulkxfer = NULL;
+		/* DF-5613: leave no dangling chunk pointer / chunk lists for
+		 * a concurrent fw_read()/fw_write() to walk after the free */
+		q->stproc = NULL;
+		STAILQ_INIT(&q->stvalid);
+		STAILQ_INIT(&q->stfree);
+		STAILQ_INIT(&q->stdma);
 		q->flag &= ~FWXFERQ_EXTBUF;
 		q->psize = 0;
 		q->maxq = FWMAXQUEUE;
@@ -278,6 +284,8 @@
 		return (EIO);
 
 readloop:
+	if (ir->buf == NULL)	/* DF-5613: queue torn down under us */
+		return (EIO);
 	xfer = STAILQ_FIRST(&ir->q);
 	if (ir->stproc == NULL) {
 		/* iso bulkxfer */
@@ -364,6 +372,8 @@
 	if (it == NULL || it->buf == NULL)
 		return (EIO);
 isoloop:
+	if (it->buf == NULL)	/* DF-5613: queue torn down under us */
+		return (EIO);
 	if (it->stproc == NULL) {
 		it->stproc = STAILQ_FIRST(&it->stfree);
 		if (it->stproc != NULL) {
@@ -446,19 +456,29 @@
 	switch (ap->a_cmd) {
 	case FW_STSTREAM:
 		if (it == NULL) {
+			crit_enter();
 			for (i = 0; i < fc->nisodma; i ++) {
 				it = fc->it[i];
 				if ((it->flag & FWXFERQ_OPEN) == 0)
 					 break;
 	                }	
 			if (i >= fc->nisodma) {
+				crit_exit();
 				err = EBUSY;
 				break;
 			}
+			/* DF-5613: claim the xferq atomically before the
+			 * sleeping fwdev_allocbuf() -- two opens must not
+			 * end up owning (and tearing down) the same queue. */
+			it->flag |= FWXFERQ_OPEN;
+			crit_exit();
 			err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
-			if (err)
+			if (err) {
+				crit_enter();
+				it->flag &= ~FWXFERQ_OPEN;
+				crit_exit();
 				break;
-			it->flag |=  FWXFERQ_OPEN;
+			}
 		}
 		it->flag &= ~0xff;
 		it->flag |= (0x3f & ichreq->ch);
@@ -474,19 +494,27 @@
 		break;
 	case FW_SRSTREAM:
 		if (ir == NULL) {
+			crit_enter();
 			for (i = 0; i < fc->nisodma; i ++) {
 				ir = fc->ir[i];
 				if ((ir->flag & FWXFERQ_OPEN) == 0)
 					break;
 			}	
 			if (i >= fc->nisodma) {
+				crit_exit();
 				err = EBUSY;
 				break;
 			}
+			/* DF-5613: atomic claim, see FW_STSTREAM */
+			ir->flag |= FWXFERQ_OPEN;
+			crit_exit();
 			err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
-			if (err)
+			if (err) {
+				crit_enter();
+				ir->flag &= ~FWXFERQ_OPEN;
+				crit_exit();
 				break;
-			ir->flag |=  FWXFERQ_OPEN;
+			}
 		}
 		ir->flag &= ~0xff;
 		ir->flag |= (0x3f & ichreq->ch);