I've discovered that there's a use-after-free bug that is triggered by
multiply-opened tty(4) devices where one process uses FIOSETOWN and then
exits.  It occurs, for example, when first ntpd(8) opens a GPS_NMEA(0)
refclock (/dev/gps0 -> /dev/ttyU0), and subsequently gpsd(8) opens the
same tty.  ntpd(8) does a FIOSETOWN ioctl on this tty soon after startup
that sets tp->t_pgrp.  (gpsd does not seem to explicitly FIOSETOWN or
TIOCSCTTY.)  Subsequently when you stop the ntpd process (with
gpsd still running) tp->t_pgrp points to freed chunk of memory.  Soon,
ttysigintr() tries to send SIGIO to this process group, and things
explode (the symptom may vary, as the pgrp memory could have been
re-used, or kASan may have intercepted it, or you just segfault in the
kernel).

I see that the TIOCSCTTY case in ttioctl() calls proc_sesshold(), but
the FIOSETOWN or TIOCSPGRP cases, which are vaguely similar, do not.

The attached patch prevents the UAF bug I observed, but I'm not confident
it's correct; and I'm even less confident for the TIOCSPGRP case which I
was unable to test.  I am mostly concerned it may result in a memory leak,
or at worst a deadlock, but perhaps that's better than crashing the kernel.

        Jonathan Kollasch
Index: src/sys/kern/tty.c
===================================================================
RCS file: /cvsroot/src/sys/kern/tty.c,v
retrieving revision 1.312
diff -d -u -a -p -r1.312 tty.c
--- src/sys/kern/tty.c	7 Dec 2023 09:00:32 -0000	1.312
+++ src/sys/kern/tty.c	24 Apr 2024 01:58:14 -0000
@@ -1441,6 +1441,7 @@ unlock_constty:	mutex_exit(&constty_lock
 			return (EPERM);
 		}
 		mutex_spin_enter(&tty_lock);
+		proc_sesshold(pgrp->pg_session);
 		tp->t_pgrp = pgrp;
 		mutex_spin_exit(&tty_lock);
 		mutex_exit(&proc_lock);
@@ -1464,6 +1465,7 @@ unlock_constty:	mutex_exit(&constty_lock
 			return (EPERM);
 		}
 		mutex_spin_enter(&tty_lock);
+		proc_sesshold(pgrp->pg_session);
 		tp->t_pgrp = pgrp;
 		mutex_spin_exit(&tty_lock);
 		mutex_exit(&proc_lock);

Reply via email to