X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/0a7de7458d150b5d4dffc935ba399be265ef0a1a..a991bd8d3e7fe02dbca0644054bab73c5b75324a:/bsd/kern/tty.c

diff --git a/bsd/kern/tty.c b/bsd/kern/tty.c
index 417357add..fbb861b00 100644
--- a/bsd/kern/tty.c
+++ b/bsd/kern/tty.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997-2017 Apple Inc. All rights reserved.
+ * Copyright (c) 1997-2019 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -138,6 +138,9 @@ static void     ttydeallocate(struct tty *tp);
 static int isctty(proc_t p, struct tty  *tp);
 static int isctty_sp(proc_t p, struct tty  *tp, struct session *sessp);
 
+__private_extern__ void termios32to64(struct termios32 *in, struct user_termios *out);
+__private_extern__ void termios64to32(struct user_termios *in, struct termios32 *out);
+
 /*
  * Table with character classes and parity. The 8th bit indicates parity,
  * the 7th bit indicates the character is an alphameric or underscore (for
@@ -226,7 +229,7 @@ static u_char const char_type[] = {
 #define I_HIGH_WATER    (TTYHOG - 2 * 256)      /* XXX */
 #define I_LOW_WATER     ((TTYHOG - 2 * 256) * 7 / 8)    /* XXX */
 
-static void
+__private_extern__ void
 termios32to64(struct termios32 *in, struct user_termios *out)
 {
 	out->c_iflag = (user_tcflag_t)in->c_iflag;
@@ -241,19 +244,19 @@ termios32to64(struct termios32 *in, struct user_termios *out)
 	out->c_ospeed = (user_speed_t)in->c_ospeed;
 }
 
-static void
+__private_extern__ void
 termios64to32(struct user_termios *in, struct termios32 *out)
 {
-	out->c_iflag = (tcflag_t)in->c_iflag;
-	out->c_oflag = (tcflag_t)in->c_oflag;
-	out->c_cflag = (tcflag_t)in->c_cflag;
-	out->c_lflag = (tcflag_t)in->c_lflag;
+	out->c_iflag = (uint32_t)in->c_iflag;
+	out->c_oflag = (uint32_t)in->c_oflag;
+	out->c_cflag = (uint32_t)in->c_cflag;
+	out->c_lflag = (uint32_t)in->c_lflag;
 
 	/* bcopy is OK, since this type is ILP32/LP64 size invariant */
 	bcopy(in->c_cc, out->c_cc, sizeof(in->c_cc));
 
-	out->c_ispeed = (speed_t)in->c_ispeed;
-	out->c_ospeed = (speed_t)in->c_ospeed;
+	out->c_ispeed = (uint32_t)MIN(in->c_ispeed, UINT32_MAX);
+	out->c_ospeed = (uint32_t)MIN(in->c_ospeed, UINT32_MAX);
 }
 
 
@@ -934,44 +937,6 @@ ttyoutput(int c, struct tty *tp)
 	return -1;
 }
 
-/*
- * Sets the tty state to not allow any more changes of foreground process
- * group. This is required to be done so that a subsequent revoke on a vnode
- * is able to always successfully complete.
- *
- * Locks :   Assumes tty_lock held on entry
- */
-void
-ttysetpgrphup(struct tty *tp)
-{
-	TTY_LOCK_OWNED(tp);     /* debug assert */
-	SET(tp->t_state, TS_PGRPHUP);
-	/*
-	 * Also wake up sleeping readers which may or may not belong to the
-	 * current foreground process group.
-	 *
-	 * This forces any non-fg readers (which entered read when
-	 * that process group was in the fg) to return with EIO (if they're
-	 * catching SIGTTIN or with SIGTTIN). The ones which do belong to the fg
-	 * process group will promptly go back to sleep and get a SIGHUP shortly
-	 * This would normally happen as part of the close in revoke but if
-	 * there is a sleeping reader from a non-fg process group we never get
-	 * to the close because the sleeping reader holds an iocount on the
-	 * vnode of the terminal which is going to get revoked->reclaimed.
-	 */
-	wakeup(TSA_HUP_OR_INPUT(tp));
-}
-
-/*
- * Locks : Assumes tty lock held on entry
- */
-void
-ttyclrpgrphup(struct tty *tp)
-{
-	TTY_LOCK_OWNED(tp);     /* debug assert */
-	CLR(tp->t_state, TS_PGRPHUP);
-}
-
 /*
  * ttioctl
  *
@@ -1163,9 +1128,8 @@ ttioctl_locked(struct tty *tp, u_long cmd, caddr_t data, int flag, proc_t p)
 	case TIOCSCONS: {
 		/* Set current console device to this line */
 		data = (caddr_t) &bogusData;
-
-		/* No break - Fall through to BSD code */
 	}
+		OS_FALLTHROUGH;
 	case TIOCCONS: {                        /* become virtual console */
 		if (*(int *)data) {
 			if (constty && constty != tp &&
@@ -1510,19 +1474,7 @@ ttioctl_locked(struct tty *tp, u_long cmd, caddr_t data, int flag, proc_t p)
 			error = EPERM;
 			goto out;
 		}
-		/*
-		 * The session leader is going away and is possibly going to revoke
-		 * the terminal, we can't change the process group when that is the
-		 * case.
-		 */
-		if (ISSET(tp->t_state, TS_PGRPHUP)) {
-			if (sessp != SESSION_NULL) {
-				session_rele(sessp);
-			}
-			pg_rele(pgrp);
-			error = EPERM;
-			goto out;
-		}
+
 		proc_list_lock();
 		oldpg = tp->t_pgrp;
 		tp->t_pgrp = pgrp;
@@ -1534,7 +1486,7 @@ ttioctl_locked(struct tty *tp, u_long cmd, caddr_t data, int flag, proc_t p)
 		 * process group.
 		 *
 		 * ttwakeup() isn't called because the readers aren't getting
-		 * woken up becuse there is something to read but to force
+		 * woken up because there is something to read but to force
 		 * the re-evaluation of their foreground process group status.
 		 *
 		 * Ordinarily leaving these readers waiting wouldn't be an issue
@@ -1581,6 +1533,19 @@ ttioctl_locked(struct tty *tp, u_long cmd, caddr_t data, int flag, proc_t p)
 	case TIOCGDRAINWAIT:
 		*(int *)data = tp->t_timeout / hz;
 		break;
+	case TIOCREVOKE:
+		SET(tp->t_state, TS_REVOKE);
+		tp->t_gen++;
+		/*
+		 * At this time, only this wait channel is woken up as only
+		 * ttread has been problematic. It is possible we may need
+		 * to add wake up other tty wait addresses as well.
+		 */
+		wakeup(TSA_HUP_OR_INPUT(tp));
+		break;
+	case TIOCREVOKECLEAR:
+		CLR(tp->t_state, TS_REVOKE);
+		break;
 	default:
 		error = ttcompat(tp, cmd, data, flag, p);
 		goto out;
@@ -2082,9 +2047,10 @@ loop:
 	}
 
 	/*
-	 * Signal the process if it's in the background.
+	 * Signal the process if it's in the background. If the terminal is
+	 * getting revoked, everybody is in the background.
 	 */
-	if (isbackground(p, tp)) {
+	if (isbackground(p, tp) || ISSET(tp->t_state, TS_REVOKE)) {
 		if ((p->p_sigignore & sigmask(SIGTTIN)) ||
 		    (ut->uu_sigmask & sigmask(SIGTTIN)) ||
 		    p->p_lflag & P_LPPWAIT) {
@@ -2147,7 +2113,7 @@ loop:
 		int m = cc[VMIN];
 		long t = cc[VTIME];
 		struct timeval timecopy;
-		struct timeval etime = {0, 0};  /* protected by !has_etime */
+		struct timeval etime = {.tv_sec = 0, .tv_usec = 0};     /* protected by !has_etime */
 
 		/*
 		 * Check each of the four combinations.
@@ -2179,20 +2145,13 @@ loop:
 				goto read;
 			}
 			microuptime(&timecopy);
-			if (!has_etime) {
-				/* first character, start timer */
+			if (!has_etime || qp->c_cc > last_cc) {
+				/* first character or got a character, start timer */
 				has_etime = 1;
 
 				etime.tv_sec = t / 1000000;
-				etime.tv_usec = (t - (etime.tv_sec * 1000000));
-				timeradd(&etime, &timecopy, &etime);
-
-				slp = t;
-			} else if (qp->c_cc > last_cc) {
-				/* got a character, restart timer */
-
-				etime.tv_sec = t / 1000000;
-				etime.tv_usec = (t - (etime.tv_sec * 1000000));
+				etime.tv_usec =
+				    (__darwin_suseconds_t)(t - (etime.tv_sec * 1000000));
 				timeradd(&etime, &timecopy, &etime);
 
 				slp = t;
@@ -2214,7 +2173,8 @@ loop:
 				has_etime = 1;
 
 				etime.tv_sec = t / 1000000;
-				etime.tv_usec = (t - (etime.tv_sec * 1000000));
+				etime.tv_usec =
+				    (__darwin_suseconds_t)(t - (etime.tv_sec * 1000000));
 				timeradd(&etime, &timecopy, &etime);
 
 				slp = t;
@@ -2273,8 +2233,13 @@ read:
 	for (;;) {
 		char ibuf[IBUFSIZ];
 		int icc;
+		ssize_t size = uio_resid(uio);
+		if (size < 0) {
+			error = ERANGE;
+			break;
+		}
 
-		icc = MIN(uio_resid(uio), IBUFSIZ);
+		icc = (int)MIN(size, IBUFSIZ);
 		icc = q_to_b(qp, (u_char *)ibuf, icc);
 		if (icc <= 0) {
 			if (first) {
@@ -2509,7 +2474,12 @@ loop:
 		 * leftover from last time.
 		 */
 		if (cc == 0) {
-			cc = MIN(uio_resid(uio), OBUFSIZ);
+			ssize_t size = uio_resid(uio);
+			if (size < 0) {
+				error = ERANGE;
+				break;
+			}
+			cc = (int)MIN((size_t)size, OBUFSIZ);
 			cp = obuf;
 			error = uiomove(cp, cc, uio);
 			if (error) {
@@ -2530,8 +2500,8 @@ loop:
 			if (!ISSET(tp->t_oflag, OPOST)) {
 				ce = cc;
 			} else {
-				ce = cc - scanc((u_int)cc, (u_char *)cp,
-				    char_type, CCLASSMASK);
+				ce = (int)((size_t)cc - scanc((size_t)cc,
+				    (u_char *)cp, char_type, CCLASSMASK));
 				/*
 				 * If ce is zero, then we're processing
 				 * a special character through ttyoutput.
@@ -2806,6 +2776,16 @@ ttyecho(int c, struct tty *tp)
 	(void)ttyoutput(c, tp);
 }
 
+static void
+ttwakeup_knote(struct selinfo *sip, long hint)
+{
+	if ((sip->si_flags & SI_KNPOSTING) == 0) {
+		sip->si_flags |= SI_KNPOSTING;
+		KNOTE(&sip->si_note, hint);
+		sip->si_flags &= ~SI_KNPOSTING;
+	}
+}
+
 
 /*
  * Wake up any readers on a tty.
@@ -2818,7 +2798,7 @@ ttwakeup(struct tty *tp)
 	TTY_LOCK_OWNED(tp);     /* debug assert */
 
 	selwakeup(&tp->t_rsel);
-	KNOTE(&tp->t_rsel.si_note, 1);
+	ttwakeup_knote(&tp->t_rsel, 0);
 	if (ISSET(tp->t_state, TS_ASYNC)) {
 		/*
 		 * XXX: Callers may not revalidate it the tty is closed
@@ -2850,7 +2830,7 @@ ttwwakeup(struct tty *tp)
 
 	if (tp->t_outq.c_cc <= tp->t_lowat) {
 		selwakeup(&tp->t_wsel);
-		KNOTE(&tp->t_wsel.si_note, 1);
+		ttwakeup_knote(&tp->t_wsel, 0);
 	}
 	if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) ==
 	    TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) {
@@ -2895,7 +2875,7 @@ ttspeedtab(int speed, struct speedtab *table)
 void
 ttsetwater(struct tty *tp)
 {
-	int cps;
+	speed_t cps;
 	unsigned int x;
 
 	TTY_LOCK_OWNED(tp);     /* debug assert */
@@ -2903,7 +2883,9 @@ ttsetwater(struct tty *tp)
 #define CLAMP(x, h, l)  ((x) > h ? h : ((x) < l) ? l : (x))
 
 	cps = tp->t_ospeed / 10;
-	tp->t_lowat = x = CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT);
+	static_assert(TTMAXLOWAT <= UINT_MAX, "max low water fits in unsigned int");
+	static_assert(TTMINLOWAT <= UINT_MAX, "min low water fits in unsigned int");
+	tp->t_lowat = x = (unsigned int)CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT);
 	x += cps;
 	x = CLAMP(x, TTMAXHIWAT, TTMINHIWAT);
 	tp->t_hiwat = roundup(x, CBSIZE);
@@ -3030,7 +3012,6 @@ ttyinfo_locked(struct tty *tp)
 		break;
 	}
 	calcru(pick, &utime, &stime, NULL);
-	proc_rele(pick);
 
 	/* Print command, pid, state, utime, and stime */
 	ttyprintf(tp, " cmd: %s %d %s %ld.%02du %ld.%02ds\n",
@@ -3039,6 +3020,8 @@ ttyinfo_locked(struct tty *tp)
 	    state,
 	    (long)utime.tv_sec, utime.tv_usec / 10000,
 	    (long)stime.tv_sec, stime.tv_usec / 10000);
+
+	proc_rele(pick);
 	tp->t_rocount = 0;
 }
 
@@ -3185,6 +3168,10 @@ ttysleep(struct tty *tp, void *chan, int pri, const char *wmesg, int timo)
 
 	TTY_LOCK_OWNED(tp);
 
+	if (tp->t_state & TS_REVOKE) {
+		return ERESTART;
+	}
+
 	gen = tp->t_gen;
 	/* Use of msleep0() avoids conversion timo/timespec/timo */
 	error = msleep0(chan, &tp->t_lock, pri, wmesg, timo, (int (*)(int))0);
@@ -3311,11 +3298,11 @@ isctty_sp(proc_t p, struct tty  *tp, struct session *sessp)
 }
 
 
-static int  filt_ttyattach(struct knote *kn, struct kevent_internal_s *kev);
+static int  filt_ttyattach(struct knote *kn, struct kevent_qos_s *kev);
 static void filt_ttydetach(struct knote *kn);
 static int  filt_ttyevent(struct knote *kn, long hint);
-static int  filt_ttytouch(struct knote *kn, struct kevent_internal_s *kev);
-static int  filt_ttyprocess(struct knote *kn, struct filt_process_s *data, struct kevent_internal_s *kev);
+static int  filt_ttytouch(struct knote *kn, struct kevent_qos_s *kev);
+static int  filt_ttyprocess(struct knote *kn, struct kevent_qos_s *kev);
 
 SECURITY_READ_ONLY_EARLY(struct filterops) tty_filtops = {
 	.f_isfd    = 1,
@@ -3331,31 +3318,35 @@ SECURITY_READ_ONLY_EARLY(struct filterops) tty_filtops = {
  * or written.
  */
 static int
-filt_tty_common(struct knote *kn, struct tty *tp)
+filt_tty_common(struct knote *kn, struct kevent_qos_s *kev, struct tty *tp)
 {
 	int retval = 0;
+	int64_t data = 0;
 
 	TTY_LOCK_OWNED(tp); /* debug assert */
 
-	if (tp->t_state & TS_ZOMBIE) {
-		kn->kn_flags |= EV_EOF;
-		return 1;
-	}
-
-	switch (knote_get_seltype(kn)) {
-	case FREAD:
-		retval = ttnread(tp);
+	switch (kn->kn_filter) {
+	case EVFILT_READ:
+		/*
+		 * ttnread can change the tty state,
+		 * hence must be done upfront, before any other check.
+		 */
+		data = ttnread(tp);
+		retval = (data != 0);
 		break;
-	case FWRITE:
+	case EVFILT_WRITE:
 		if ((tp->t_outq.c_cc <= tp->t_lowat) &&
 		    (tp->t_state & TS_CONNECTED)) {
-			retval = tp->t_hiwat - tp->t_outq.c_cc;
+			data = tp->t_hiwat - tp->t_outq.c_cc;
+			retval = (data != 0);
 		}
 		break;
+	default:
+		panic("tty kevent: unexpected filter: %d, kn = %p, tty = %p",
+		    kn->kn_filter, kn, tp);
+		break;
 	}
 
-	kn->kn_data = retval;
-
 	/*
 	 * TODO(mwidmann, jandrus): For native knote low watermark support,
 	 * check the kn_sfflags for NOTE_LOWAT and check against kn_sdata.
@@ -3364,6 +3355,16 @@ filt_tty_common(struct knote *kn, struct tty *tp)
 	 *        (kn->kn_data >= kn->kn_sdata) : kn->kn_data;
 	 */
 
+	if (tp->t_state & TS_ZOMBIE) {
+		kn->kn_flags |= EV_EOF;
+	}
+	if (kn->kn_flags & EV_EOF) {
+		retval = 1;
+	}
+	if (retval && kev) {
+		knote_fill_kevent(kn, kev, data);
+	}
+
 	return retval;
 }
 
@@ -3415,24 +3416,6 @@ tty_from_knote(struct knote *kn)
 	return (struct tty *)kn->kn_hook;
 }
 
-/*
- * Try to lock the TTY structure associated with a knote.
- *
- * On success, this function returns a locked TTY structure.  Otherwise, NULL is
- * returned.
- */
-__attribute__((warn_unused_result))
-static struct tty *
-tty_lock_from_knote(struct knote *kn)
-{
-	struct tty *tp = tty_from_knote(kn);
-	if (tp) {
-		tty_lock(tp);
-	}
-
-	return tp;
-}
-
 /*
  * Set the knote's struct tty to the kn_hook field.
  *
@@ -3460,7 +3443,7 @@ tty_set_knote_hook(struct knote *kn)
 	uth = get_bsdthread_info(current_thread());
 
 	ctx = vfs_context_current();
-	vp = (vnode_t)kn->kn_fp->f_fglob->fg_data;
+	vp = (vnode_t)kn->kn_fp->fp_glob->fg_data;
 
 	/*
 	 * Reserve a link element to avoid potential allocation under
@@ -3538,7 +3521,7 @@ out:
 }
 
 static int
-filt_ttyattach(struct knote *kn, __unused struct kevent_internal_s *kev)
+filt_ttyattach(struct knote *kn, __unused struct kevent_qos_s *kev)
 {
 	int selres = 0;
 	struct tty *tp;
@@ -3566,19 +3549,18 @@ filt_ttyattach(struct knote *kn, __unused struct kevent_internal_s *kev)
 	/*
 	 * Attach the knote to selinfo's klist.
 	 */
-	tp = tty_lock_from_knote(kn);
-	if (!tp) {
-		knote_set_error(kn, ENOENT);
-		return 0;
-	}
+	tp = tty_from_knote(kn);
+	tty_lock(tp);
 
-	switch (knote_get_seltype(kn)) {
-	case FREAD:
+	switch (kn->kn_filter) {
+	case EVFILT_READ:
 		KNOTE_ATTACH(&tp->t_rsel.si_note, kn);
 		break;
-	case FWRITE:
+	case EVFILT_WRITE:
 		KNOTE_ATTACH(&tp->t_wsel.si_note, kn);
 		break;
+	default:
+		panic("invalid knote %p attach, filter: %d", kn, kn->kn_filter);
 	}
 
 	tty_unlock(tp);
@@ -3589,28 +3571,22 @@ filt_ttyattach(struct knote *kn, __unused struct kevent_internal_s *kev)
 static void
 filt_ttydetach(struct knote *kn)
 {
-	struct tty *tp;
+	struct tty *tp = tty_from_knote(kn);
 
-	tp = tty_lock_from_knote(kn);
-	if (!tp) {
-		knote_set_error(kn, ENOENT);
-		return;
-	}
+	tty_lock(tp);
 
-	struct selinfo *si = NULL;
-	switch (knote_get_seltype(kn)) {
-	case FREAD:
-		si = &tp->t_rsel;
+	switch (kn->kn_filter) {
+	case EVFILT_READ:
+		KNOTE_DETACH(&tp->t_rsel.si_note, kn);
 		break;
-	case FWRITE:
-		si = &tp->t_wsel;
+	case EVFILT_WRITE:
+		KNOTE_DETACH(&tp->t_wsel.si_note, kn);
+		break;
+	default:
+		panic("invalid knote %p detach, filter: %d", kn, kn->kn_filter);
 		break;
-		/* knote_get_seltype will panic on default */
 	}
 
-	KNOTE_DETACH(&si->si_note, kn);
-	kn->kn_hook = NULL;
-
 	tty_unlock(tp);
 	ttyfree(tp);
 }
@@ -3618,52 +3594,34 @@ filt_ttydetach(struct knote *kn)
 static int
 filt_ttyevent(struct knote *kn, long hint)
 {
+	struct tty *tp = tty_from_knote(kn);
 	int ret;
-	struct tty *tp;
-	bool revoked = hint & NOTE_REVOKE;
-	hint &= ~NOTE_REVOKE;
-
-	tp = tty_from_knote(kn);
-	if (!tp) {
-		knote_set_error(kn, ENOENT);
-		return 0;
-	}
 
-	if (!hint) {
-		tty_lock(tp);
-	}
+	TTY_LOCK_OWNED(tp);
 
-	if (revoked) {
+	if (hint & NOTE_REVOKE) {
 		kn->kn_flags |= EV_EOF | EV_ONESHOT;
 		ret = 1;
 	} else {
-		ret = filt_tty_common(kn, tp);
-	}
-
-	if (!hint) {
-		tty_unlock(tp);
+		ret = filt_tty_common(kn, NULL, tp);
 	}
 
 	return ret;
 }
 
 static int
-filt_ttytouch(struct knote *kn, struct kevent_internal_s *kev)
+filt_ttytouch(struct knote *kn, struct kevent_qos_s *kev)
 {
-	struct tty *tp;
+	struct tty *tp = tty_from_knote(kn);
 	int res = 0;
 
-	tp = tty_lock_from_knote(kn);
-	if (!tp) {
-		knote_set_error(kn, ENOENT);
-		return 0;
-	}
+	tty_lock(tp);
 
 	kn->kn_sdata = kev->data;
 	kn->kn_sfflags = kev->fflags;
 
 	if (kn->kn_vnode_kqok) {
-		res = filt_tty_common(kn, tp);
+		res = filt_tty_common(kn, NULL, tp);
 	}
 
 	tty_unlock(tp);
@@ -3672,26 +3630,14 @@ filt_ttytouch(struct knote *kn, struct kevent_internal_s *kev)
 }
 
 static int
-filt_ttyprocess(struct knote *kn, __unused struct filt_process_s *data, struct kevent_internal_s *kev)
+filt_ttyprocess(struct knote *kn, struct kevent_qos_s *kev)
 {
-	struct tty *tp;
+	struct tty *tp = tty_from_knote(kn);
 	int res;
 
-	tp = tty_lock_from_knote(kn);
-	if (!tp) {
-		knote_set_error(kn, ENOENT);
-		return 0;
-	}
-
-	res = filt_tty_common(kn, tp);
+	tty_lock(tp);
 
-	if (res) {
-		*kev = kn->kn_kevent;
-		if (kn->kn_flags & EV_CLEAR) {
-			kn->kn_fflags = 0;
-			kn->kn_data = 0;
-		}
-	}
+	res = filt_tty_common(kn, kev, tp);
 
 	tty_unlock(tp);