Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #define _GNU_SOURCE
0003 #include <errno.h>
0004 #include <fcntl.h>
0005 #include <sched.h>
0006 #include <stdbool.h>
0007 #include <stdio.h>
0008 #include <stdlib.h>
0009 #include <string.h>
0010 #include <unistd.h>
0011 #include <asm/ioctls.h>
0012 #include <sys/mount.h>
0013 #include <sys/wait.h>
0014 #include "../kselftest.h"
0015 
0016 static bool terminal_dup2(int duplicate, int original)
0017 {
0018     int ret;
0019 
0020     ret = dup2(duplicate, original);
0021     if (ret < 0)
0022         return false;
0023 
0024     return true;
0025 }
0026 
0027 static int terminal_set_stdfds(int fd)
0028 {
0029     int i;
0030 
0031     if (fd < 0)
0032         return 0;
0033 
0034     for (i = 0; i < 3; i++)
0035         if (!terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO,
0036                            STDERR_FILENO}[i]))
0037             return -1;
0038 
0039     return 0;
0040 }
0041 
0042 static int login_pty(int fd)
0043 {
0044     int ret;
0045 
0046     setsid();
0047 
0048     ret = ioctl(fd, TIOCSCTTY, NULL);
0049     if (ret < 0)
0050         return -1;
0051 
0052     ret = terminal_set_stdfds(fd);
0053     if (ret < 0)
0054         return -1;
0055 
0056     if (fd > STDERR_FILENO)
0057         close(fd);
0058 
0059     return 0;
0060 }
0061 
0062 static int wait_for_pid(pid_t pid)
0063 {
0064     int status, ret;
0065 
0066 again:
0067     ret = waitpid(pid, &status, 0);
0068     if (ret == -1) {
0069         if (errno == EINTR)
0070             goto again;
0071         return -1;
0072     }
0073     if (ret != pid)
0074         goto again;
0075 
0076     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
0077         return -1;
0078 
0079     return 0;
0080 }
0081 
0082 static int resolve_procfd_symlink(int fd, char *buf, size_t buflen)
0083 {
0084     int ret;
0085     char procfd[4096];
0086 
0087     ret = snprintf(procfd, 4096, "/proc/self/fd/%d", fd);
0088     if (ret < 0 || ret >= 4096)
0089         return -1;
0090 
0091     ret = readlink(procfd, buf, buflen);
0092     if (ret < 0 || (size_t)ret >= buflen)
0093         return -1;
0094 
0095     buf[ret] = '\0';
0096 
0097     return 0;
0098 }
0099 
0100 static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents)
0101 {
0102     int ret;
0103     int master = -1, slave = -1, fret = -1;
0104 
0105     master = open(ptmx, O_RDWR | O_NOCTTY | O_CLOEXEC);
0106     if (master < 0) {
0107         fprintf(stderr, "Failed to open \"%s\": %s\n", ptmx,
0108             strerror(errno));
0109         return -1;
0110     }
0111 
0112     /*
0113      * grantpt() makes assumptions about /dev/pts/ so ignore it. It's also
0114      * not really needed.
0115      */
0116     ret = unlockpt(master);
0117     if (ret < 0) {
0118         fprintf(stderr, "Failed to unlock terminal\n");
0119         goto do_cleanup;
0120     }
0121 
0122 #ifdef TIOCGPTPEER
0123     slave = ioctl(master, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC);
0124 #endif
0125     if (slave < 0) {
0126         if (errno == EINVAL) {
0127             fprintf(stderr, "TIOCGPTPEER is not supported. "
0128                     "Skipping test.\n");
0129             fret = KSFT_SKIP;
0130         } else {
0131             fprintf(stderr,
0132                 "Failed to perform TIOCGPTPEER ioctl\n");
0133             fret = EXIT_FAILURE;
0134         }
0135         goto do_cleanup;
0136     }
0137 
0138     pid_t pid = fork();
0139     if (pid < 0)
0140         goto do_cleanup;
0141 
0142     if (pid == 0) {
0143         char buf[4096];
0144 
0145         ret = login_pty(slave);
0146         if (ret < 0) {
0147             fprintf(stderr, "Failed to setup terminal\n");
0148             _exit(EXIT_FAILURE);
0149         }
0150 
0151         ret = resolve_procfd_symlink(STDIN_FILENO, buf, sizeof(buf));
0152         if (ret < 0) {
0153             fprintf(stderr, "Failed to retrieve pathname of pts "
0154                     "slave file descriptor\n");
0155             _exit(EXIT_FAILURE);
0156         }
0157 
0158         if (strncmp(expected_procfd_contents, buf,
0159                 strlen(expected_procfd_contents)) != 0) {
0160             fprintf(stderr, "Received invalid contents for "
0161                     "\"/proc/<pid>/fd/%d\" symlink: %s\n",
0162                     STDIN_FILENO, buf);
0163             _exit(-1);
0164         }
0165 
0166         fprintf(stderr, "Contents of \"/proc/<pid>/fd/%d\" "
0167                 "symlink are valid: %s\n", STDIN_FILENO, buf);
0168 
0169         _exit(EXIT_SUCCESS);
0170     }
0171 
0172     ret = wait_for_pid(pid);
0173     if (ret < 0)
0174         goto do_cleanup;
0175 
0176     fret = EXIT_SUCCESS;
0177 
0178 do_cleanup:
0179     if (master >= 0)
0180         close(master);
0181     if (slave >= 0)
0182         close(slave);
0183 
0184     return fret;
0185 }
0186 
0187 static int verify_non_standard_devpts_mount(void)
0188 {
0189     char *mntpoint;
0190     int ret = -1;
0191     char devpts[] = P_tmpdir "/devpts_fs_XXXXXX";
0192     char ptmx[] = P_tmpdir "/devpts_fs_XXXXXX/ptmx";
0193 
0194     ret = umount("/dev/pts");
0195     if (ret < 0) {
0196         fprintf(stderr, "Failed to unmount \"/dev/pts\": %s\n",
0197                 strerror(errno));
0198         return -1;
0199     }
0200 
0201     (void)umount("/dev/ptmx");
0202 
0203     mntpoint = mkdtemp(devpts);
0204     if (!mntpoint) {
0205         fprintf(stderr, "Failed to create temporary mountpoint: %s\n",
0206                  strerror(errno));
0207         return -1;
0208     }
0209 
0210     ret = mount("devpts", mntpoint, "devpts", MS_NOSUID | MS_NOEXEC,
0211             "newinstance,ptmxmode=0666,mode=0620,gid=5");
0212     if (ret < 0) {
0213         fprintf(stderr, "Failed to mount devpts fs to \"%s\" in new "
0214                 "mount namespace: %s\n", mntpoint,
0215                 strerror(errno));
0216         unlink(mntpoint);
0217         return -1;
0218     }
0219 
0220     ret = snprintf(ptmx, sizeof(ptmx), "%s/ptmx", devpts);
0221     if (ret < 0 || (size_t)ret >= sizeof(ptmx)) {
0222         unlink(mntpoint);
0223         return -1;
0224     }
0225 
0226     ret = do_tiocgptpeer(ptmx, mntpoint);
0227     unlink(mntpoint);
0228     if (ret < 0)
0229         return -1;
0230 
0231     return 0;
0232 }
0233 
0234 static int verify_ptmx_bind_mount(void)
0235 {
0236     int ret;
0237 
0238     ret = mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_BIND, NULL);
0239     if (ret < 0) {
0240         fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
0241                 "\"/dev/ptmx\" mount namespace\n");
0242         return -1;
0243     }
0244 
0245     ret = do_tiocgptpeer("/dev/ptmx", "/dev/pts/");
0246     if (ret < 0)
0247         return -1;
0248 
0249     return 0;
0250 }
0251 
0252 static int verify_invalid_ptmx_bind_mount(void)
0253 {
0254     int ret;
0255     char mntpoint_fd;
0256     char ptmx[] = P_tmpdir "/devpts_ptmx_XXXXXX";
0257 
0258     mntpoint_fd = mkstemp(ptmx);
0259     if (mntpoint_fd < 0) {
0260         fprintf(stderr, "Failed to create temporary directory: %s\n",
0261                  strerror(errno));
0262         return -1;
0263     }
0264 
0265     ret = mount("/dev/pts/ptmx", ptmx, NULL, MS_BIND, NULL);
0266     close(mntpoint_fd);
0267     if (ret < 0) {
0268         fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
0269                 "\"%s\" mount namespace\n", ptmx);
0270         return -1;
0271     }
0272 
0273     ret = do_tiocgptpeer(ptmx, "/dev/pts/");
0274     if (ret == 0)
0275         return -1;
0276 
0277     return 0;
0278 }
0279 
0280 int main(int argc, char *argv[])
0281 {
0282     int ret;
0283 
0284     if (!isatty(STDIN_FILENO)) {
0285         fprintf(stderr, "Standard input file descriptor is not attached "
0286                 "to a terminal. Skipping test\n");
0287         exit(KSFT_SKIP);
0288     }
0289 
0290     ret = unshare(CLONE_NEWNS);
0291     if (ret < 0) {
0292         fprintf(stderr, "Failed to unshare mount namespace\n");
0293         exit(EXIT_FAILURE);
0294     }
0295 
0296     ret = mount("", "/", NULL, MS_PRIVATE | MS_REC, 0);
0297     if (ret < 0) {
0298         fprintf(stderr, "Failed to make \"/\" MS_PRIVATE in new mount "
0299                 "namespace\n");
0300         exit(EXIT_FAILURE);
0301     }
0302 
0303     ret = verify_ptmx_bind_mount();
0304     if (ret < 0)
0305         exit(EXIT_FAILURE);
0306 
0307     ret = verify_invalid_ptmx_bind_mount();
0308     if (ret < 0)
0309         exit(EXIT_FAILURE);
0310 
0311     ret = verify_non_standard_devpts_mount();
0312     if (ret < 0)
0313         exit(EXIT_FAILURE);
0314 
0315     exit(EXIT_SUCCESS);
0316 }