DragonFlyBSD Kernel Audit
DF-2896 / fix.diff
← back to finding ↓ download raw
--- a/sys/kern/tty_cons.c
+++ b/sys/kern/tty_cons.c
@@ -455,15 +455,38 @@ cnread(struct dev_read_args *ap)
 static int
 cnwrite(struct dev_write_args *ap)
 {
 	struct uio *uio = ap->a_uio;
+	struct tty *tp;
 	cdev_t dev;
+	int error;
 
 	if (cn_tab == NULL || cn_fwd_ops == NULL) {
 		uio->uio_resid = 0; /* dump the data */
 		return (0);
 	}
-	if (constty)
-		dev = constty->t_dev;
-	else
-		dev = cn_tab->cn_dev;
+	/*
+	 * Serialize against constty teardown.  The teardown path
+	 * (ttyclose() clearing constty, and pti_done() clearing
+	 * tp->t_dev and destroy_dev()ing the cdev) runs under
+	 * tp->t_token.  Capture the cdev under the same token and
+	 * hold the token across the forward so the cdev cannot be
+	 * destroyed between the capture and dev_doperate().
+	 * (constty-capable ttys -- ptys and the console drivers --
+	 * have persistent struct tty storage, so the token itself
+	 * stays valid.)
+	 */
+	tp = constty;
+	dev = NULL;
+	if (tp) {
+		lwkt_gettoken(&tp->t_token);
+		if (constty != tp || tp->t_dev == NULL) {
+			lwkt_reltoken(&tp->t_token);
+			tp = NULL;
+		} else {
+			dev = tp->t_dev;
+		}
+	}
+	if (dev == NULL)
+		dev = cn_tab->cn_dev;
 	log_console(uio);
 	ap->a_head.a_dev = dev;
-	return (dev_doperate(&ap->a_head));
+	error = dev_doperate(&ap->a_head);
+	if (tp)
+		lwkt_reltoken(&tp->t_token);
+	return (error);
 }