]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/tty.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / bsd / kern / tty.c
index 417357add10f375b235de1723b738356b6ae26fd..fbb861b00521fb0ba0c821e407a4f5b0e23f8cad 100644 (file)
@@ -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);