git: 44077c07f19f - releng/14.3 - tty: Avoid leaving dangling pointers in tty_drop_ctty()
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 21 Apr 2026 15:46:01 UTC
The branch releng/14.3 has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=44077c07f19f5e60593dcd87f7c2c33ea7e5ca69
commit 44077c07f19f5e60593dcd87f7c2c33ea7e5ca69
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-03-23 15:22:48 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-04-21 15:45:50 +0000
tty: Avoid leaving dangling pointers in tty_drop_ctty()
The TIOCNOTTY handler detaches the calling process from its controlling
terminal. It clears the link from the session to the tty, but not the
pointers from the tty to the session and process group. This means that
sess_release() doesn't call tty_rel_sess(), and that pgdelete() doesn't
call tty_rel_pgrp(), so the pointers are left dangling.
Fix this by clearing pointers in tty_drop_ctty(). Add a standalone
regression test.
Approved by: so
Security: FreeBSD-SA-26:10.tty
Security: CVE-2026-5398
Reported by: Nicholas Carlini <npc@anthropic.com>
Reviewed by: kib, kevans
Fixes: 1b50b999f9b5 ("tty: implement TIOCNOTTY")
Differential Revision: https://reviews.freebsd.org/D56046
---
sys/kern/tty.c | 4 +++
tests/sys/kern/Makefile | 1 +
tests/sys/kern/tiocnotty.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 87 insertions(+)
diff --git a/sys/kern/tty.c b/sys/kern/tty.c
index b1b3b268d0e9..a968762d6167 100644
--- a/sys/kern/tty.c
+++ b/sys/kern/tty.c
@@ -1262,6 +1262,10 @@ tty_drop_ctty(struct tty *tp, struct proc *p)
session->s_ttydp = NULL;
SESS_UNLOCK(session);
+ if (tp->t_session == session) {
+ tp->t_session = NULL;
+ tp->t_pgrp = NULL;
+ }
tp->t_sessioncnt--;
p->p_flag &= ~P_CONTROLT;
PROC_UNLOCK(p);
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
index eb5ea8c3c549..ac7e4898dfa0 100644
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -41,6 +41,7 @@ ATF_TESTS_C+= subr_physmem_test
PLAIN_TESTS_C+= subr_unit_test
ATF_TESTS_C+= sysctl_kern_proc
ATF_TESTS_C+= sys_getrandom
+PLAIN_TESTS_C+= tiocnotty
ATF_TESTS_C+= tty_pts
ATF_TESTS_C+= unix_dgram
ATF_TESTS_C+= unix_passfd_dgram
diff --git a/tests/sys/kern/tiocnotty.c b/tests/sys/kern/tiocnotty.c
new file mode 100644
index 000000000000..2581f976b2ef
--- /dev/null
+++ b/tests/sys/kern/tiocnotty.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2026 Mark Johnston <markj@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * A regression test that exercises a bug where TIOCNOTTY would leave some
+ * dangling pointers behind in the controlling terminal structure.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ int master, slave, status;
+ pid_t child;
+
+ master = posix_openpt(O_RDWR | O_NOCTTY);
+ if (master < 0)
+ err(1, "posix_openpt");
+ if (grantpt(master) < 0)
+ err(1, "grantpt");
+ if (unlockpt(master) < 0)
+ err(1, "unlockpt");
+
+ child = fork();
+ if (child < 0)
+ err(1, "fork");
+ if (child == 0) {
+ if (setsid() < 0)
+ err(1, "setsid");
+ slave = open(ptsname(master), O_RDWR | O_NOCTTY);
+ if (slave < 0)
+ err(2, "open");
+ if (ioctl(slave, TIOCSCTTY, 0) < 0)
+ err(3, "ioctl(TIOCSCTTY)");
+ /* Detach ourselves from the controlling terminal. */
+ if (ioctl(slave, TIOCNOTTY, 0) < 0)
+ err(4, "ioctl(TIOCNOTTY)");
+ _exit(0);
+ }
+
+ if (waitpid(child, &status, 0) < 0)
+ err(1, "waitpid");
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ errx(1, "child exited with status %d", WEXITSTATUS(status));
+
+ child = fork();
+ if (child < 0)
+ err(1, "fork");
+ if (child == 0) {
+ struct winsize winsz;
+
+ if (setsid() < 0)
+ err(1, "setsid");
+ slave = open(ptsname(master), O_RDWR | O_NOCTTY);
+ if (slave < 0)
+ err(2, "open");
+ /* Dereferences dangling t_pgrp pointer in the terminal. */
+ memset(&winsz, 0xff, sizeof(winsz));
+ if (ioctl(slave, TIOCSWINSZ, &winsz) < 0)
+ err(3, "ioctl(TIOCSWINSZ)");
+ /* Dereferences dangling t_session pointer in the terminal. */
+ if (ioctl(slave, TIOCSCTTY, 0) < 0)
+ err(4, "ioctl(TIOCSCTTY)");
+ _exit(0);
+ }
+
+ if (waitpid(child, &status, 0) < 0)
+ err(1, "waitpid");
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ errx(1, "child exited with status %d", WEXITSTATUS(status));
+}