Path: blob/master/tools/testing/selftests/filesystems/devpts_pts.c
26298 views
// SPDX-License-Identifier: GPL-2.01#define _GNU_SOURCE2#include <errno.h>3#include <fcntl.h>4#include <sched.h>5#include <stdbool.h>6#include <stdio.h>7#include <stdlib.h>8#include <string.h>9#include <unistd.h>10#include <asm/ioctls.h>11#include <sys/mount.h>12#include <sys/wait.h>13#include "../kselftest.h"1415static bool terminal_dup2(int duplicate, int original)16{17int ret;1819ret = dup2(duplicate, original);20if (ret < 0)21return false;2223return true;24}2526static int terminal_set_stdfds(int fd)27{28int i;2930if (fd < 0)31return 0;3233for (i = 0; i < 3; i++)34if (!terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO,35STDERR_FILENO}[i]))36return -1;3738return 0;39}4041static int login_pty(int fd)42{43int ret;4445setsid();4647ret = ioctl(fd, TIOCSCTTY, NULL);48if (ret < 0)49return -1;5051ret = terminal_set_stdfds(fd);52if (ret < 0)53return -1;5455if (fd > STDERR_FILENO)56close(fd);5758return 0;59}6061static int wait_for_pid(pid_t pid)62{63int status, ret;6465again:66ret = waitpid(pid, &status, 0);67if (ret == -1) {68if (errno == EINTR)69goto again;70return -1;71}72if (ret != pid)73goto again;7475if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)76return -1;7778return 0;79}8081static int resolve_procfd_symlink(int fd, char *buf, size_t buflen)82{83int ret;84char procfd[4096];8586ret = snprintf(procfd, 4096, "/proc/self/fd/%d", fd);87if (ret < 0 || ret >= 4096)88return -1;8990ret = readlink(procfd, buf, buflen);91if (ret < 0 || (size_t)ret >= buflen)92return -1;9394buf[ret] = '\0';9596return 0;97}9899static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents)100{101int ret;102int master = -1, slave = -1, fret = -1;103104master = open(ptmx, O_RDWR | O_NOCTTY | O_CLOEXEC);105if (master < 0) {106fprintf(stderr, "Failed to open \"%s\": %s\n", ptmx,107strerror(errno));108return -1;109}110111/*112* grantpt() makes assumptions about /dev/pts/ so ignore it. It's also113* not really needed.114*/115ret = unlockpt(master);116if (ret < 0) {117fprintf(stderr, "Failed to unlock terminal\n");118goto do_cleanup;119}120121#ifdef TIOCGPTPEER122slave = ioctl(master, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC);123#endif124if (slave < 0) {125if (errno == EINVAL) {126fprintf(stderr, "TIOCGPTPEER is not supported. "127"Skipping test.\n");128fret = KSFT_SKIP;129} else {130fprintf(stderr,131"Failed to perform TIOCGPTPEER ioctl\n");132fret = EXIT_FAILURE;133}134goto do_cleanup;135}136137pid_t pid = fork();138if (pid < 0)139goto do_cleanup;140141if (pid == 0) {142char buf[4096];143144ret = login_pty(slave);145if (ret < 0) {146fprintf(stderr, "Failed to setup terminal\n");147_exit(EXIT_FAILURE);148}149150ret = resolve_procfd_symlink(STDIN_FILENO, buf, sizeof(buf));151if (ret < 0) {152fprintf(stderr, "Failed to retrieve pathname of pts "153"slave file descriptor\n");154_exit(EXIT_FAILURE);155}156157if (strncmp(expected_procfd_contents, buf,158strlen(expected_procfd_contents)) != 0) {159fprintf(stderr, "Received invalid contents for "160"\"/proc/<pid>/fd/%d\" symlink: %s\n",161STDIN_FILENO, buf);162_exit(-1);163}164165fprintf(stderr, "Contents of \"/proc/<pid>/fd/%d\" "166"symlink are valid: %s\n", STDIN_FILENO, buf);167168_exit(EXIT_SUCCESS);169}170171ret = wait_for_pid(pid);172if (ret < 0)173goto do_cleanup;174175fret = EXIT_SUCCESS;176177do_cleanup:178if (master >= 0)179close(master);180if (slave >= 0)181close(slave);182183return fret;184}185186static int verify_non_standard_devpts_mount(void)187{188char *mntpoint;189int ret = -1;190char devpts[] = P_tmpdir "/devpts_fs_XXXXXX";191char ptmx[] = P_tmpdir "/devpts_fs_XXXXXX/ptmx";192193ret = umount("/dev/pts");194if (ret < 0) {195fprintf(stderr, "Failed to unmount \"/dev/pts\": %s\n",196strerror(errno));197return -1;198}199200(void)umount("/dev/ptmx");201202mntpoint = mkdtemp(devpts);203if (!mntpoint) {204fprintf(stderr, "Failed to create temporary mountpoint: %s\n",205strerror(errno));206return -1;207}208209ret = mount("devpts", mntpoint, "devpts", MS_NOSUID | MS_NOEXEC,210"newinstance,ptmxmode=0666,mode=0620,gid=5");211if (ret < 0) {212fprintf(stderr, "Failed to mount devpts fs to \"%s\" in new "213"mount namespace: %s\n", mntpoint,214strerror(errno));215unlink(mntpoint);216return -1;217}218219ret = snprintf(ptmx, sizeof(ptmx), "%s/ptmx", devpts);220if (ret < 0 || (size_t)ret >= sizeof(ptmx)) {221unlink(mntpoint);222return -1;223}224225ret = do_tiocgptpeer(ptmx, mntpoint);226unlink(mntpoint);227if (ret < 0)228return -1;229230return 0;231}232233static int verify_ptmx_bind_mount(void)234{235int ret;236237ret = mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_BIND, NULL);238if (ret < 0) {239fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "240"\"/dev/ptmx\" mount namespace\n");241return -1;242}243244ret = do_tiocgptpeer("/dev/ptmx", "/dev/pts/");245if (ret < 0)246return -1;247248return 0;249}250251static int verify_invalid_ptmx_bind_mount(void)252{253int ret;254char mntpoint_fd;255char ptmx[] = P_tmpdir "/devpts_ptmx_XXXXXX";256257mntpoint_fd = mkstemp(ptmx);258if (mntpoint_fd < 0) {259fprintf(stderr, "Failed to create temporary directory: %s\n",260strerror(errno));261return -1;262}263264ret = mount("/dev/pts/ptmx", ptmx, NULL, MS_BIND, NULL);265close(mntpoint_fd);266if (ret < 0) {267fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "268"\"%s\" mount namespace\n", ptmx);269return -1;270}271272ret = do_tiocgptpeer(ptmx, "/dev/pts/");273if (ret == 0)274return -1;275276return 0;277}278279int main(int argc, char *argv[])280{281int ret;282283if (!isatty(STDIN_FILENO)) {284fprintf(stderr, "Standard input file descriptor is not attached "285"to a terminal. Skipping test\n");286exit(KSFT_SKIP);287}288289ret = unshare(CLONE_NEWNS);290if (ret < 0) {291fprintf(stderr, "Failed to unshare mount namespace\n");292exit(EXIT_FAILURE);293}294295ret = mount("", "/", NULL, MS_PRIVATE | MS_REC, 0);296if (ret < 0) {297fprintf(stderr, "Failed to make \"/\" MS_PRIVATE in new mount "298"namespace\n");299exit(EXIT_FAILURE);300}301302ret = verify_ptmx_bind_mount();303if (ret < 0)304exit(EXIT_FAILURE);305306ret = verify_invalid_ptmx_bind_mount();307if (ret < 0)308exit(EXIT_FAILURE);309310ret = verify_non_standard_devpts_mount();311if (ret < 0)312exit(EXIT_FAILURE);313314exit(EXIT_SUCCESS);315}316317318