--- 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);