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.
This commit is contained in:
Liav A 2022-02-15 21:39:01 +02:00 committed by Andreas Kling
parent 5ffe2f117c
commit de7566c2c4
Notes: sideshowbarker 2024-07-17 16:53:08 +09:00
2 changed files with 103 additions and 4 deletions

View file

@ -24,8 +24,11 @@
#include <stdlib.h>
#include <string.h>
#include <sys/internals.h>
#include <sys/ioctl.h>
#include <sys/ioctl_numbers.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/wait.h>
#include <syscall.h>
#include <unistd.h>
@ -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;

View file

@ -4,12 +4,14 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/ScopeGuard.h>
#include <AK/ScopedValueRollback.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <alloca.h>
#include <assert.h>
#include <bits/pthread_integration.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@ -22,6 +24,8 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <syscall.h>
#include <termios.h>
@ -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];