1 // SPDX-License-Identifier: GPL-2.0
11 #include <asm/ioctls.h>
12 #include <sys/mount.h>
14 #include "../kselftest.h"
16 static bool terminal_dup2(int duplicate
, int original
)
20 ret
= dup2(duplicate
, original
);
27 static int terminal_set_stdfds(int fd
)
34 for (i
= 0; i
< 3; i
++)
35 if (!terminal_dup2(fd
, (int[]){STDIN_FILENO
, STDOUT_FILENO
,
42 static int login_pty(int fd
)
48 ret
= ioctl(fd
, TIOCSCTTY
, NULL
);
52 ret
= terminal_set_stdfds(fd
);
56 if (fd
> STDERR_FILENO
)
62 static int wait_for_pid(pid_t pid
)
67 ret
= waitpid(pid
, &status
, 0);
76 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0)
82 static int resolve_procfd_symlink(int fd
, char *buf
, size_t buflen
)
87 ret
= snprintf(procfd
, 4096, "/proc/self/fd/%d", fd
);
88 if (ret
< 0 || ret
>= 4096)
91 ret
= readlink(procfd
, buf
, buflen
);
92 if (ret
< 0 || (size_t)ret
>= buflen
)
100 static int do_tiocgptpeer(char *ptmx
, char *expected_procfd_contents
)
103 int master
= -1, slave
= -1, fret
= -1;
105 master
= open(ptmx
, O_RDWR
| O_NOCTTY
| O_CLOEXEC
);
107 fprintf(stderr
, "Failed to open \"%s\": %s\n", ptmx
,
113 * grantpt() makes assumptions about /dev/pts/ so ignore it. It's also
116 ret
= unlockpt(master
);
118 fprintf(stderr
, "Failed to unlock terminal\n");
123 slave
= ioctl(master
, TIOCGPTPEER
, O_RDWR
| O_NOCTTY
| O_CLOEXEC
);
126 if (errno
== EINVAL
) {
127 fprintf(stderr
, "TIOCGPTPEER is not supported. "
132 "Failed to perform TIOCGPTPEER ioctl\n");
145 ret
= login_pty(slave
);
147 fprintf(stderr
, "Failed to setup terminal\n");
151 ret
= resolve_procfd_symlink(STDIN_FILENO
, buf
, sizeof(buf
));
153 fprintf(stderr
, "Failed to retrieve pathname of pts "
154 "slave file descriptor\n");
158 if (strncmp(expected_procfd_contents
, buf
,
159 strlen(expected_procfd_contents
)) != 0) {
160 fprintf(stderr
, "Received invalid contents for "
161 "\"/proc/<pid>/fd/%d\" symlink: %s\n",
166 fprintf(stderr
, "Contents of \"/proc/<pid>/fd/%d\" "
167 "symlink are valid: %s\n", STDIN_FILENO
, buf
);
172 ret
= wait_for_pid(pid
);
187 static int verify_non_standard_devpts_mount(void)
191 char devpts
[] = P_tmpdir
"/devpts_fs_XXXXXX";
192 char ptmx
[] = P_tmpdir
"/devpts_fs_XXXXXX/ptmx";
194 ret
= umount("/dev/pts");
196 fprintf(stderr
, "Failed to unmount \"/dev/pts\": %s\n",
201 (void)umount("/dev/ptmx");
203 mntpoint
= mkdtemp(devpts
);
205 fprintf(stderr
, "Failed to create temporary mountpoint: %s\n",
210 ret
= mount("devpts", mntpoint
, "devpts", MS_NOSUID
| MS_NOEXEC
,
211 "newinstance,ptmxmode=0666,mode=0620,gid=5");
213 fprintf(stderr
, "Failed to mount devpts fs to \"%s\" in new "
214 "mount namespace: %s\n", mntpoint
,
220 ret
= snprintf(ptmx
, sizeof(ptmx
), "%s/ptmx", devpts
);
221 if (ret
< 0 || (size_t)ret
>= sizeof(ptmx
)) {
226 ret
= do_tiocgptpeer(ptmx
, mntpoint
);
234 static int verify_ptmx_bind_mount(void)
238 ret
= mount("/dev/pts/ptmx", "/dev/ptmx", NULL
, MS_BIND
, NULL
);
240 fprintf(stderr
, "Failed to bind mount \"/dev/pts/ptmx\" to "
241 "\"/dev/ptmx\" mount namespace\n");
245 ret
= do_tiocgptpeer("/dev/ptmx", "/dev/pts/");
252 static int verify_invalid_ptmx_bind_mount(void)
256 char ptmx
[] = P_tmpdir
"/devpts_ptmx_XXXXXX";
258 mntpoint_fd
= mkstemp(ptmx
);
259 if (mntpoint_fd
< 0) {
260 fprintf(stderr
, "Failed to create temporary directory: %s\n",
265 ret
= mount("/dev/pts/ptmx", ptmx
, NULL
, MS_BIND
, NULL
);
268 fprintf(stderr
, "Failed to bind mount \"/dev/pts/ptmx\" to "
269 "\"%s\" mount namespace\n", ptmx
);
273 ret
= do_tiocgptpeer(ptmx
, "/dev/pts/");
280 int main(int argc
, char *argv
[])
284 if (!isatty(STDIN_FILENO
)) {
285 fprintf(stderr
, "Standard input file descriptor is not attached "
286 "to a terminal. Skipping test\n");
290 ret
= unshare(CLONE_NEWNS
);
292 fprintf(stderr
, "Failed to unshare mount namespace\n");
296 ret
= mount("", "/", NULL
, MS_PRIVATE
| MS_REC
, 0);
298 fprintf(stderr
, "Failed to make \"/\" MS_PRIVATE in new mount "
303 ret
= verify_ptmx_bind_mount();
307 ret
= verify_invalid_ptmx_bind_mount();
311 ret
= verify_non_standard_devpts_mount();