⬢ DragonFlyBSD Kernel Audit
DF-4669 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/misc/snp/snp.c
+++ b/sys/dev/misc/snp/snp.c
@@ -134,6 +134,19 @@
 
 	lwkt_gettoken(&snp_token);
 	snp = tp->t_sc;
+	if (snp == NULL) {
+		/*
+		 * The snoop line discipline can be installed on a tty via
+		 * TIOCSETD without any SNPSTTY attach.  t_sc is NULL in
+		 * that case; do not run the snoop shutdown path on it.
+		 * (DF-4668)
+		 */
+		lwkt_reltoken(&snp_token);
+		lwkt_gettoken(&tp->t_token);
+		error = ttylclose(tp, flag);
+		lwkt_reltoken(&tp->t_token);
+		return (error);
+	}
 	error = snp_down(snp);
 	if (error != 0) {
 		lwkt_reltoken(&snp_token);
@@ -557,10 +570,27 @@
 			return (EBUSY);
 		}
 
-		if (snp->snp_target == NULL) {
-			tpo = snp->snp_tty;
-			if (tpo)
+		/*
+		 * Properly un-poison the previous target tty before
+		 * re-attaching.  The old guard (snp->snp_target == NULL)
+		 * was never true on re-attach because snp_target and
+		 * snp_tty are only ever set and cleared together, so the
+		 * old tty kept t_sc pointing at this snoop and its line
+		 * discipline stuck on snooplinedisc; after the snp device
+		 * is closed that leaves a dangling t_sc which snplclose
+		 * later dereferences (double free).  (DF-4669)
+		 */
+		tpo = snp->snp_tty;
+		if (tpo != NULL && tpo != tp) {
+			lwkt_gettoken(&tpo->t_token);
+			if ((tpo->t_sc == (caddr_t)snp) &&
+			    (tpo->t_state & TS_SNOOP) &&
+			    tpo->t_line == snooplinedisc) {
+				tpo->t_sc = NULL;
 				tpo->t_state &= ~TS_SNOOP;
+				tpo->t_line = snp->snp_olddisc;
+			}
+			lwkt_reltoken(&tpo->t_token);
 		}
 
 		tp->t_sc = (caddr_t)snp;