From de7566c2c49ca30785e90194f91bc81f907a5f70 Mon Sep 17 00:00:00 2001 From: Liav A Date: Tue, 15 Feb 2022 21:39:01 +0200 Subject: [PATCH] LibC: Don't rely on ptsname and ttyname syscalls Instead, to determine these values (both the pts name and tty name), use other methods. For determining the new name of the allocated psuedo terminal, use ioctl on a file descriptor we got after opening /dev/ptmx with the TIOCGPTN option. For determining the name of TTY, we enumerate both /dev/pts and /dev directories to find matching inode number and matching device mode. --- Userland/Libraries/LibC/stdlib.cpp | 38 +++++++++++++++- Userland/Libraries/LibC/unistd.cpp | 69 +++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 4 deletions(-) diff --git a/Userland/Libraries/LibC/stdlib.cpp b/Userland/Libraries/LibC/stdlib.cpp index d56dfc03d18..d9e7d73ead3 100644 --- a/Userland/Libraries/LibC/stdlib.cpp +++ b/Userland/Libraries/LibC/stdlib.cpp @@ -24,8 +24,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -724,8 +727,39 @@ char* ptsname(int fd) int ptsname_r(int fd, char* buffer, size_t size) { - int rc = syscall(SC_ptsname, fd, buffer, size); - __RETURN_WITH_ERRNO(rc, rc, -1); + struct stat stat; + if (fstat(fd, &stat) < 0) + return -1; + + StringBuilder devpts_path_builder; + devpts_path_builder.append("/dev/pts/"sv); + + int master_pty_index = 0; + // Note: When the user opens a PTY from /dev/ptmx with posix_openpt(), the open file descriptor + // points to /dev/ptmx, (major number is 5 and minor number is 2), but internally + // in the kernel, it points to a new MasterPTY device. When we do ioctl with TIOCGPTN option + // on the open file descriptor, it actually asks the MasterPTY what is the assigned index + // of it when the PTYMultiplexer created it. + if (ioctl(fd, TIOCGPTN, &master_pty_index) < 0) + return -1; + + if (master_pty_index < 0) { + errno = EINVAL; + return -1; + } + + devpts_path_builder.appendff("{:d}", master_pty_index); + if (devpts_path_builder.length() > size) { + errno = ERANGE; + return -1; + } + memset(buffer, 0, devpts_path_builder.length() + 1); + auto full_devpts_path_string = devpts_path_builder.build(); + if (!full_devpts_path_string.copy_characters_to_buffer(buffer, size)) { + errno = ERANGE; + return -1; + } + return 0; } static unsigned long s_next_rand = 1; diff --git a/Userland/Libraries/LibC/unistd.cpp b/Userland/Libraries/LibC/unistd.cpp index b05f7e62e80..c7caea4678b 100644 --- a/Userland/Libraries/LibC/unistd.cpp +++ b/Userland/Libraries/LibC/unistd.cpp @@ -4,12 +4,14 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include #include #include #include +#include #include #include #include @@ -22,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -398,11 +402,72 @@ ssize_t pwrite(int fd, const void* buf, size_t count, off_t offset) return nwritten; } +// Note: Be sure to send to directory_name parameter a directory name ended with trailing slash. +static int ttyname_r_for_directory(const char* directory_name, dev_t device_mode, ino_t inode_number, char* buffer, size_t size) +{ + DIR* dirstream = opendir(directory_name); + if (!dirstream) { + return -1; + } + + auto close_dir_stream_on_exit = ScopeGuard([dirstream] { + closedir(dirstream); + }); + + struct dirent* entry = nullptr; + char* name_path = nullptr; + + // FIXME: Use LibCore DirIterator here instead + while ((entry = readdir(dirstream)) != nullptr) { + if (((ino_t)entry->d_ino == inode_number) + && strcmp(entry->d_name, "stdin") + && strcmp(entry->d_name, "stdout") + && strcmp(entry->d_name, "stderr")) { + + size_t name_length = strlen(directory_name) + strlen(entry->d_name) + 1; + + if (name_length > size) { + errno = ERANGE; + return -1; + } + + name_path = (char*)malloc(name_length); + memset(name_path, 0, name_length); + memcpy(name_path, directory_name, strlen(directory_name)); + memcpy(&name_path[strlen(directory_name)], entry->d_name, strlen(entry->d_name)); + struct stat st; + if (lstat(name_path, &st) < 0) { + free(name_path); + continue; + } + + if (device_mode == st.st_rdev) { + memset(buffer, 0, name_length); + memcpy(buffer, name_path, name_length); + free(name_path); + return 0; + } + } + } + free(name_path); + return -1; +} + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/ttyname_r.html int ttyname_r(int fd, char* buffer, size_t size) { - int rc = syscall(SC_ttyname, fd, buffer, size); - __RETURN_WITH_ERRNO(rc, rc, -1); + struct stat stat; + if (fstat(fd, &stat) < 0) + return -1; + dev_t major_minor_numbers = stat.st_rdev; + ino_t inode_number = stat.st_ino; + if (ttyname_r_for_directory("/dev/", major_minor_numbers, inode_number, buffer, size) < 0) { + if (ttyname_r_for_directory("/dev/pts/", major_minor_numbers, inode_number, buffer, size) < 0) { + errno = ENOTTY; + return -1; + } + } + return 0; } static char ttyname_buf[32];