⬢ DragonFlyBSD Kernel Audit
DF-5191 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/serial/sio/sio_pccard.c b/sys/dev/serial/sio/sio_pccard.c
--- a/sys/dev/serial/sio/sio_pccard.c
+++ b/sys/dev/serial/sio/sio_pccard.c
@@ -114,22 +114,45 @@
 static int
 sio_pccard_detach(device_t dev)
 {
 	struct com_s	*com;
+	struct tty	*tp;

 	com = (struct com_s *) device_get_softc(dev);
 	if (com == NULL) {
 		device_printf(dev, "NULL com in siounload\n");
 		return (0);
 	}
+
+	/*
+	 * DF-5191: every sio core path that touches com->gone, com->tp or
+	 * com->ibuf runs under tty_token (sioopen, sioclose, sioparam,
+	 * siosetwater "called with tty_token held").  Detach must take the
+	 * same token or it races open/close in flight and double-frees
+	 * com->ibuf.
+	 *
+	 * Sequence: publish com->gone under the token (stops new work),
+	 * DROP the token before draining the callouts (their callbacks
+	 * acquire tty_token themselves: siodtrwakeup, siobusycheck --
+	 * draining under the token would deadlock), then re-acquire for
+	 * the teardown.  The com->gone guards added to the callbacks keep
+	 * them from re-arming after the drain (DF-2191).
+	 */
+	lwkt_gettoken(&tty_token);
 	com->gone = 1;
+	lwkt_reltoken(&tty_token);
+
+	callout_drain(&com->dtr_ch);		/* DF-2191 */
+	callout_drain(&com->busy_ch);		/* DF-2191 */
+
+	lwkt_gettoken(&tty_token);
+	crit_enter();
+	tp = com->tp;
 	if (com->irqres) {
 		bus_teardown_intr(dev, com->irqres, com->cookie);
 		bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres);
+		com->irqres = NULL;
 	}
 	if (com->ioportres) {
 		bus_release_resource(dev, SYS_RES_IOPORT, 0, com->ioportres);
+		com->ioportres = NULL;
 	}
-	if (com->tp && (com->tp->t_state & TS_ISOPEN)) {
+	if (tp && (tp->t_state & TS_ISOPEN)) {
 		device_printf(dev, "still open, forcing close\n");
-		ttyclose(com->tp);
+		ttyclose(tp);
 	} else {
 		if (com->ibuf != NULL) {
 			kfree(com->ibuf, M_DEVBUF);
+			com->ibuf = NULL;	/* DF-3902 */
+			com->ibufold = NULL;
 		}
 	}
+	crit_exit();
+	lwkt_reltoken(&tty_token);
 	device_printf(dev, "unloaded\n");
 	return (0);
 }
diff --git a/sys/dev/serial/sio/sio.c b/sys/dev/serial/sio/sio.c
--- a/sys/dev/serial/sio/sio.c
+++ b/sys/dev/serial/sio/sio.c
@@ -1605,6 +1605,9 @@ siobusycheck(void *chan)
 	lwkt_gettoken(&tty_token);
 	com = (struct com_s *)chan;
+	if (com->gone) {		/* DF-2191: detach in flight */
+		lwkt_reltoken(&tty_token);
+		return;
+	}
 
 	/*
 	 * Clear TS_BUSY if low-level output is complete.
 	 * spl locking is sufficient because siointr1() does not set CS_BUSY.
@@ -1660,6 +1663,9 @@ siodtrwakeup(void *chan)
 	lwkt_gettoken(&tty_token);
 	com = (struct com_s *)chan;
+	if (com->gone) {		/* DF-2191: detach in flight */
+		lwkt_reltoken(&tty_token);
+		return;
+	}
 	com->state &= ~CS_DTR_OFF;
 	wakeup(&com->dtr_wait);
 	lwkt_reltoken(&tty_token);