# HG changeset patch # User Todd C. Miller # Date 1496243671 21600 # Node ID 15a46f4007dde8e819dd2c70e670a529bbb9d312 # Parent 6f3d9816541ba84055ae5aec6ff9d9523c2a96f3 A command name may also contain newline characters so read /proc/self/stat until EOF. It is not legal for /proc/self/stat to contain embedded NUL bytes so treat the file as corrupt if we see any. With help from Qualys. This is not exploitable due to the /dev traversal changes in sudo 1.8.20p1 (thanks Solar!). diff -r 6f3d9816541b -r 15a46f4007dd src/ttyname.c --- a/src/ttyname.c Tue May 30 10:44:11 2017 -0600 +++ b/src/ttyname.c Wed May 31 09:14:31 2017 -0600 @@ -452,25 +452,37 @@ get_process_ttyname(char *name, size_t namelen) { const char path[] = "/proc/self/stat"; - char *line = NULL; + char *cp, buf[1024]; char *ret = NULL; - size_t linesize = 0; int serrno = errno; - ssize_t len; - FILE *fp; + ssize_t nread; + int fd; debug_decl(get_process_ttyname, SUDO_DEBUG_UTIL) - /* Try to determine the tty from tty_nr in /proc/self/stat. */ - if ((fp = fopen(path, "r")) != NULL) { - len = getline(&line, &linesize, fp); - fclose(fp); - if (len != -1) { + /* + * Try to determine the tty from tty_nr in /proc/self/stat. + * Ignore /proc/self/stat if it contains embedded NUL bytes. + */ + if ((fd = open(path, O_RDONLY | O_NOFOLLOW)) != -1) { + cp = buf; + while ((nread = read(fd, cp, buf + sizeof(buf) - cp)) != 0) { + if (nread == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + break; + } + cp += nread; + if (cp >= buf + sizeof(buf)) + break; + } + if (nread == 0 && memchr(buf, '\0', cp - buf) == NULL) { /* * Field 7 is the tty dev (0 if no tty). - * Since the process name at field 2 "(comm)" may include spaces, - * start at the last ')' found. + * Since the process name at field 2 "(comm)" may include + * whitespace (including newlines), start at the last ')' found. */ - char *cp = strrchr(line, ')'); + *cp = '\0'; + cp = strrchr(buf, ')'); if (cp != NULL) { char *ep = cp; const char *errstr; @@ -501,7 +513,8 @@ errno = ENOENT; done: - free(line); + if (fd != -1) + close(fd); if (ret == NULL) sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, "unable to resolve tty via %s", path);