123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- // SPDX-License-Identifier: GPL-2.0
- #define _GNU_SOURCE
- #include <errno.h>
- #include <fcntl.h>
- #include <sched.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <asm/ioctls.h>
- #include <sys/mount.h>
- #include <sys/wait.h>
- #include "../kselftest.h"
- static bool terminal_dup2(int duplicate, int original)
- {
- int ret;
- ret = dup2(duplicate, original);
- if (ret < 0)
- return false;
- return true;
- }
- static int terminal_set_stdfds(int fd)
- {
- int i;
- if (fd < 0)
- return 0;
- for (i = 0; i < 3; i++)
- if (!terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO,
- STDERR_FILENO}[i]))
- return -1;
- return 0;
- }
- static int login_pty(int fd)
- {
- int ret;
- setsid();
- ret = ioctl(fd, TIOCSCTTY, NULL);
- if (ret < 0)
- return -1;
- ret = terminal_set_stdfds(fd);
- if (ret < 0)
- return -1;
- if (fd > STDERR_FILENO)
- close(fd);
- return 0;
- }
- static int wait_for_pid(pid_t pid)
- {
- int status, ret;
- again:
- ret = waitpid(pid, &status, 0);
- if (ret == -1) {
- if (errno == EINTR)
- goto again;
- return -1;
- }
- if (ret != pid)
- goto again;
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
- return -1;
- return 0;
- }
- static int resolve_procfd_symlink(int fd, char *buf, size_t buflen)
- {
- int ret;
- char procfd[4096];
- ret = snprintf(procfd, 4096, "/proc/self/fd/%d", fd);
- if (ret < 0 || ret >= 4096)
- return -1;
- ret = readlink(procfd, buf, buflen);
- if (ret < 0 || (size_t)ret >= buflen)
- return -1;
- buf[ret] = '\0';
- return 0;
- }
- static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents)
- {
- int ret;
- int master = -1, slave = -1, fret = -1;
- master = open(ptmx, O_RDWR | O_NOCTTY | O_CLOEXEC);
- if (master < 0) {
- fprintf(stderr, "Failed to open \"%s\": %s\n", ptmx,
- strerror(errno));
- return -1;
- }
- /*
- * grantpt() makes assumptions about /dev/pts/ so ignore it. It's also
- * not really needed.
- */
- ret = unlockpt(master);
- if (ret < 0) {
- fprintf(stderr, "Failed to unlock terminal\n");
- goto do_cleanup;
- }
- #ifdef TIOCGPTPEER
- slave = ioctl(master, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC);
- #endif
- if (slave < 0) {
- if (errno == EINVAL) {
- fprintf(stderr, "TIOCGPTPEER is not supported. "
- "Skipping test.\n");
- fret = KSFT_SKIP;
- } else {
- fprintf(stderr,
- "Failed to perform TIOCGPTPEER ioctl\n");
- fret = EXIT_FAILURE;
- }
- goto do_cleanup;
- }
- pid_t pid = fork();
- if (pid < 0)
- goto do_cleanup;
- if (pid == 0) {
- char buf[4096];
- ret = login_pty(slave);
- if (ret < 0) {
- fprintf(stderr, "Failed to setup terminal\n");
- _exit(EXIT_FAILURE);
- }
- ret = resolve_procfd_symlink(STDIN_FILENO, buf, sizeof(buf));
- if (ret < 0) {
- fprintf(stderr, "Failed to retrieve pathname of pts "
- "slave file descriptor\n");
- _exit(EXIT_FAILURE);
- }
- if (strncmp(expected_procfd_contents, buf,
- strlen(expected_procfd_contents)) != 0) {
- fprintf(stderr, "Received invalid contents for "
- "\"/proc/<pid>/fd/%d\" symlink: %s\n",
- STDIN_FILENO, buf);
- _exit(-1);
- }
- fprintf(stderr, "Contents of \"/proc/<pid>/fd/%d\" "
- "symlink are valid: %s\n", STDIN_FILENO, buf);
- _exit(EXIT_SUCCESS);
- }
- ret = wait_for_pid(pid);
- if (ret < 0)
- goto do_cleanup;
- fret = EXIT_SUCCESS;
- do_cleanup:
- if (master >= 0)
- close(master);
- if (slave >= 0)
- close(slave);
- return fret;
- }
- static int verify_non_standard_devpts_mount(void)
- {
- char *mntpoint;
- int ret = -1;
- char devpts[] = P_tmpdir "/devpts_fs_XXXXXX";
- char ptmx[] = P_tmpdir "/devpts_fs_XXXXXX/ptmx";
- ret = umount("/dev/pts");
- if (ret < 0) {
- fprintf(stderr, "Failed to unmount \"/dev/pts\": %s\n",
- strerror(errno));
- return -1;
- }
- (void)umount("/dev/ptmx");
- mntpoint = mkdtemp(devpts);
- if (!mntpoint) {
- fprintf(stderr, "Failed to create temporary mountpoint: %s\n",
- strerror(errno));
- return -1;
- }
- ret = mount("devpts", mntpoint, "devpts", MS_NOSUID | MS_NOEXEC,
- "newinstance,ptmxmode=0666,mode=0620,gid=5");
- if (ret < 0) {
- fprintf(stderr, "Failed to mount devpts fs to \"%s\" in new "
- "mount namespace: %s\n", mntpoint,
- strerror(errno));
- unlink(mntpoint);
- return -1;
- }
- ret = snprintf(ptmx, sizeof(ptmx), "%s/ptmx", devpts);
- if (ret < 0 || (size_t)ret >= sizeof(ptmx)) {
- unlink(mntpoint);
- return -1;
- }
- ret = do_tiocgptpeer(ptmx, mntpoint);
- unlink(mntpoint);
- if (ret < 0)
- return -1;
- return 0;
- }
- static int verify_ptmx_bind_mount(void)
- {
- int ret;
- ret = mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_BIND, NULL);
- if (ret < 0) {
- fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
- "\"/dev/ptmx\" mount namespace\n");
- return -1;
- }
- ret = do_tiocgptpeer("/dev/ptmx", "/dev/pts/");
- if (ret < 0)
- return -1;
- return 0;
- }
- static int verify_invalid_ptmx_bind_mount(void)
- {
- int ret;
- char mntpoint_fd;
- char ptmx[] = P_tmpdir "/devpts_ptmx_XXXXXX";
- mntpoint_fd = mkstemp(ptmx);
- if (mntpoint_fd < 0) {
- fprintf(stderr, "Failed to create temporary directory: %s\n",
- strerror(errno));
- return -1;
- }
- ret = mount("/dev/pts/ptmx", ptmx, NULL, MS_BIND, NULL);
- close(mntpoint_fd);
- if (ret < 0) {
- fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
- "\"%s\" mount namespace\n", ptmx);
- return -1;
- }
- ret = do_tiocgptpeer(ptmx, "/dev/pts/");
- if (ret == 0)
- return -1;
- return 0;
- }
- int main(int argc, char *argv[])
- {
- int ret;
- if (!isatty(STDIN_FILENO)) {
- fprintf(stderr, "Standard input file descriptor is not attached "
- "to a terminal. Skipping test\n");
- exit(KSFT_SKIP);
- }
- ret = unshare(CLONE_NEWNS);
- if (ret < 0) {
- fprintf(stderr, "Failed to unshare mount namespace\n");
- exit(EXIT_FAILURE);
- }
- ret = mount("", "/", NULL, MS_PRIVATE | MS_REC, 0);
- if (ret < 0) {
- fprintf(stderr, "Failed to make \"/\" MS_PRIVATE in new mount "
- "namespace\n");
- exit(EXIT_FAILURE);
- }
- ret = verify_ptmx_bind_mount();
- if (ret < 0)
- exit(EXIT_FAILURE);
- ret = verify_invalid_ptmx_bind_mount();
- if (ret < 0)
- exit(EXIT_FAILURE);
- ret = verify_non_standard_devpts_mount();
- if (ret < 0)
- exit(EXIT_FAILURE);
- exit(EXIT_SUCCESS);
- }
|