--- a/sys/kern/tty.c 2026-08-30 20:12:26.875972177 +0000 +++ b/sys/kern/tty.c 2026-08-30 20:13:08.707430133 +0000 @@ -1195,8 +1195,33 @@ otp = p->p_session->s_ttyp; p->p_session->s_ttyp = tp; p->p_flags |= P_CONTROLT; - if (otp) + if (otp) { + /* + * Fully dissociate the tty we are replacing. + * Otherwise otp->t_session keeps pointing at the + * session while s_ttyp no longer references otp; + * when the session is destroyed sess_rele() only + * repairs s_ttyp's t_session, leaving otp with a + * dangling t_session that is later dereferenced + * by ttyclearsession() and ttymodem(). + */ + if (otp != tp) { + struct pgrp *xpgrp; + + lwkt_gettoken(&otp->t_token); + if (otp->t_session == p->p_session) { + otp->t_session = NULL; + xpgrp = otp->t_pgrp; + otp->t_pgrp = NULL; + } else { + xpgrp = NULL; + } + lwkt_reltoken(&otp->t_token); + if (xpgrp) + pgrel(xpgrp); + } ttyunhold(otp); + } if (opgrp) { pgrel(opgrp); opgrp = NULL; --- a/sys/vfs/devfs/devfs_vnops.c 2026-08-30 20:12:26.879972126 +0000 +++ b/sys/vfs/devfs/devfs_vnops.c 2026-08-30 20:13:08.715430029 +0000 @@ -1544,6 +1544,18 @@ "devfs_fo_ioctl() called! for dev %s\n", dev->si_name); + /* + * A pty master must never become a controlling terminal: its + * last close clears s_ttyvp (devfs_spec_close) without running + * ttyclose(), which orphans the tty's t_session and allows the + * session to be reassigned while the first tty still points at + * it. Reject before the device ioctl runs. + */ + if (com == TIOCSCTTY && (dev_dflags(dev) & D_MASTER)) { + error = EINVAL; + goto out; + } + if (com == FIODTYPE) { *(int *)data = dev_dflags(dev) & D_TYPEMASK; error = 0; --- a/sys/kern/vfs_vnops.c 2026-08-30 20:12:26.879972126 +0000 +++ b/sys/kern/vfs_vnops.c 2026-08-30 20:13:08.727429874 +0000 @@ -1026,6 +1026,16 @@ error = 0; break; } + /* + * A pty master must never become a controlling + * terminal (see devfs_fo_ioctl); reject before the + * device ioctl runs. + */ + if (com == TIOCSCTTY && vp->v_type == VCHR && vp->v_rdev && + (dev_dflags(vp->v_rdev) & D_MASTER)) { + error = EINVAL; + break; + } error = VOP_IOCTL(vp, com, data, fp->f_flag, ucred, msg); if (error == 0 && com == TIOCSCTTY) { struct proc *p = curthread->td_proc;