0001
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
0114
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 }