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);