Message ID | 20230605183243.3493759-1-adhemerval.zanella@linaro.org |
---|---|
State | New |
Headers | show |
Series | [v2] linux: Split tst-ttyname | expand |
* Adhemerval Zanella: > The tst-ttyname may fail in container environments while trying to > mount the procfs after the unshare (test do_in_chroot_2). It is not > clear why exaclty kernel returns EPERM in this case, the container > does have CAP_SYS_CHROOT, SELinux/AppArmor is disabled, and there is > no seccomp filter. > > To avoid always reporting the test as FAIL in such scenario, the > test that uses new namespaces is moved to a new one and the failure > on the mount command is now report as UNSUPPORTED. Sorry, there's this change a well: - VERIFY (mount ("proc", "/proc", "proc", - MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) == 0); + if (mount ("proc", "/proc", "proc", + MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0) + { + /* This happens if we're trying to create a nested container, + like if the build is running under podman, and we lack + priviledges. */ + if (errno == EPERM) + _exit (EXIT_UNSUPPORTED); + else + _exit (EXIT_FAILURE); + } I wouldn't expect this based on the commit message. The systemd developers say that mounting /proc before the unshare (CLONE_NEWNS | CLONE_NEWPID) call should avoid the failure. I'd prefer to fix that in a separate change. Thanks, Florian
Florian Weimer via Libc-alpha <libc-alpha@sourceware.org> writes: > The systemd developers say that mounting /proc before the > unshare (CLONE_NEWNS | CLONE_NEWPID) call should avoid the failure. Beware doing this - /proc's contents depend on which pid namespace it's in, so that things like /proc/self and /proc/$pid work correctly. If you mount /proc and then change the pid namespace, /proc will be wrong.
On Mon, Jun 05, 2023 at 03:32:43PM -0300, Adhemerval Zanella via Libc-alpha wrote: > The tst-ttyname may fail in container environments while trying to > mount the procfs after the unshare (test do_in_chroot_2). It is not > clear why exaclty kernel returns EPERM in this case, the container > does have CAP_SYS_CHROOT, SELinux/AppArmor is disabled, and there is > no seccomp filter. If it's not yet clear why exactly EPERM is returned in this case, please re-read https://bugzilla.redhat.com/show_bug.cgi?id=2210335#c10 mentioned earlier in this thread.
On 07/06/23 18:24, Florian Weimer wrote: > * Adhemerval Zanella: > >> The tst-ttyname may fail in container environments while trying to >> mount the procfs after the unshare (test do_in_chroot_2). It is not >> clear why exaclty kernel returns EPERM in this case, the container >> does have CAP_SYS_CHROOT, SELinux/AppArmor is disabled, and there is >> no seccomp filter. >> >> To avoid always reporting the test as FAIL in such scenario, the >> test that uses new namespaces is moved to a new one and the failure >> on the mount command is now report as UNSUPPORTED. > > Sorry, there's this change a well: > > - VERIFY (mount ("proc", "/proc", "proc", > - MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) == 0); > + if (mount ("proc", "/proc", "proc", > + MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0) > + { > + /* This happens if we're trying to create a nested container, > + like if the build is running under podman, and we lack > + priviledges. */ > + if (errno == EPERM) > + _exit (EXIT_UNSUPPORTED); > + else > + _exit (EXIT_FAILURE); > + } > > I wouldn't expect this based on the commit message. > > The systemd developers say that mounting /proc before the > unshare (CLONE_NEWNS | CLONE_NEWPID) call should avoid the failure. > > I'd prefer to fix that in a separate change. Already, I can add this change in a subsequent patch. > If it's not yet clear why exactly EPERM is returned in this case, > please re-read https://bugzilla.redhat.com/show_bug.cgi?id=2210335#c10 > mentioned earlier in this thread. I have read the thread, but what is not clear to me is how to proper fix on tst-ttyname. Running on my environment I do see that after support_become_root the process mount point is indeed 'polluted' with extra procfs mount added by the container itself: /proc/self/mountinfo: 1241 1240 0:184 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw /proc/self/mountinfo: 1242 1241 0:184 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw /proc/self/mountinfo: 1243 1241 0:184 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw /proc/self/mountinfo: 1244 1241 0:184 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw /proc/self/mountinfo: 1249 1241 0:184 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw /proc/self/mountinfo: 1257 1241 0:184 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw /proc/self/mountinfo: 1258 1241 0:189 / /proc/acpi ro,relatime - tmpfs tmpfs ro,inode64 /proc/self/mountinfo: 1259 1241 0:185 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64 /proc/self/mountinfo: 1260 1241 0:185 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64 /proc/self/mountinfo: 1375 1241 0:185 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64 /proc/self/mountinfo: 1410 1241 0:190 / /proc/scsi ro,relatime - tmpfs tmpfs ro,inode64 The 'fix' that Christian has suggested is out of the scope of the test because afaiu it should be done by the container management (before spawning the process that trigger the glibc testcase). I have tried to move this test to test-container, but it circles back to the same issue. That's why I have added the UNSUPPORTED if the inner mount fails (I think I can improve the commit message).
On Thu, Jun 08, 2023 at 08:53:42AM -0300, Adhemerval Zanella Netto via Libc-alpha wrote: > > > On 07/06/23 18:24, Florian Weimer wrote: > > * Adhemerval Zanella: > > > >> The tst-ttyname may fail in container environments while trying to > >> mount the procfs after the unshare (test do_in_chroot_2). It is not > >> clear why exaclty kernel returns EPERM in this case, the container > >> does have CAP_SYS_CHROOT, SELinux/AppArmor is disabled, and there is > >> no seccomp filter. > >> > >> To avoid always reporting the test as FAIL in such scenario, the > >> test that uses new namespaces is moved to a new one and the failure > >> on the mount command is now report as UNSUPPORTED. > > > > Sorry, there's this change a well: > > > > - VERIFY (mount ("proc", "/proc", "proc", > > - MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) == 0); > > + if (mount ("proc", "/proc", "proc", > > + MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0) > > + { > > + /* This happens if we're trying to create a nested container, > > + like if the build is running under podman, and we lack > > + priviledges. */ > > + if (errno == EPERM) > > + _exit (EXIT_UNSUPPORTED); > > + else > > + _exit (EXIT_FAILURE); > > + } > > > > I wouldn't expect this based on the commit message. > > > > The systemd developers say that mounting /proc before the > > unshare (CLONE_NEWNS | CLONE_NEWPID) call should avoid the failure. > > > > I'd prefer to fix that in a separate change. > > Already, I can add this change in a subsequent patch. > > > > If it's not yet clear why exactly EPERM is returned in this case, > > please re-read https://bugzilla.redhat.com/show_bug.cgi?id=2210335#c10 > > mentioned earlier in this thread. > > I have read the thread, but what is not clear to me is how to proper fix > on tst-ttyname. Running on my environment I do see that after > support_become_root the process mount point is indeed 'polluted' with > extra procfs mount added by the container itself: > > /proc/self/mountinfo: 1241 1240 0:184 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw > /proc/self/mountinfo: 1242 1241 0:184 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw > /proc/self/mountinfo: 1243 1241 0:184 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw > /proc/self/mountinfo: 1244 1241 0:184 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw > /proc/self/mountinfo: 1249 1241 0:184 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw > /proc/self/mountinfo: 1257 1241 0:184 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw > /proc/self/mountinfo: 1258 1241 0:189 / /proc/acpi ro,relatime - tmpfs tmpfs ro,inode64 > /proc/self/mountinfo: 1259 1241 0:185 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64 > /proc/self/mountinfo: 1260 1241 0:185 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64 > /proc/self/mountinfo: 1375 1241 0:185 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64 > /proc/self/mountinfo: 1410 1241 0:190 / /proc/scsi ro,relatime - tmpfs tmpfs ro,inode64 > > The 'fix' that Christian has suggested is out of the scope of the test > because afaiu it should be done by the container management (before > spawning the process that trigger the glibc testcase). So there's a few things to consider. What I wrote in that bug report applies to unprivileged containers only - which is what the systemd-nspawn bug report was about. If you're using a privileged container things are different. So you need to figure that out first. (1) unprivileged container If this is an unprivileged container then you can test whether my fix works by trying: umount /proc/kcore If that works then the mount isn't locked. So that means you could reveal the underlying files. If that's the case then within the container manager you can do: mount -t proc proc /run/host/proc (2) privileged container (i) If this is a privileged container your container manager might simply have dropped CAP_SYS_ADMIN so you can neither mount or umount. (ii) The files and directories covering various proc files and directories are protected by an LSM profile making it impossible to unmount them. For this to be meaningful you would need to be restricted either from creating namespaces - which doesn't seem to be the case - or the LSM profile would have to restrict you from mounting procfs. If you happen to have bpftrace you can test this quickly by: # This will report the return value of security_sb_mount(). sudo bpftrace -e 'kretprobe:security_sb_mount { printf("returned: %d\n", retval); }' and then try: mount -t proc proc /mnt and see whether you get an errno.
On 09/06/23 04:49, Christian Brauner wrote: > On Thu, Jun 08, 2023 at 08:53:42AM -0300, Adhemerval Zanella Netto via Libc-alpha wrote: >> >> >> On 07/06/23 18:24, Florian Weimer wrote: >>> * Adhemerval Zanella: >>> >>>> The tst-ttyname may fail in container environments while trying to >>>> mount the procfs after the unshare (test do_in_chroot_2). It is not >>>> clear why exaclty kernel returns EPERM in this case, the container >>>> does have CAP_SYS_CHROOT, SELinux/AppArmor is disabled, and there is >>>> no seccomp filter. >>>> >>>> To avoid always reporting the test as FAIL in such scenario, the >>>> test that uses new namespaces is moved to a new one and the failure >>>> on the mount command is now report as UNSUPPORTED. >>> >>> Sorry, there's this change a well: >>> >>> - VERIFY (mount ("proc", "/proc", "proc", >>> - MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) == 0); >>> + if (mount ("proc", "/proc", "proc", >>> + MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0) >>> + { >>> + /* This happens if we're trying to create a nested container, >>> + like if the build is running under podman, and we lack >>> + priviledges. */ >>> + if (errno == EPERM) >>> + _exit (EXIT_UNSUPPORTED); >>> + else >>> + _exit (EXIT_FAILURE); >>> + } >>> >>> I wouldn't expect this based on the commit message. >>> >>> The systemd developers say that mounting /proc before the >>> unshare (CLONE_NEWNS | CLONE_NEWPID) call should avoid the failure. >>> >>> I'd prefer to fix that in a separate change. >> >> Already, I can add this change in a subsequent patch. >> >> >>> If it's not yet clear why exactly EPERM is returned in this case, >>> please re-read https://bugzilla.redhat.com/show_bug.cgi?id=2210335#c10 >>> mentioned earlier in this thread. >> >> I have read the thread, but what is not clear to me is how to proper fix >> on tst-ttyname. Running on my environment I do see that after >> support_become_root the process mount point is indeed 'polluted' with >> extra procfs mount added by the container itself: >> >> /proc/self/mountinfo: 1241 1240 0:184 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw >> /proc/self/mountinfo: 1242 1241 0:184 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw >> /proc/self/mountinfo: 1243 1241 0:184 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw >> /proc/self/mountinfo: 1244 1241 0:184 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw >> /proc/self/mountinfo: 1249 1241 0:184 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw >> /proc/self/mountinfo: 1257 1241 0:184 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw >> /proc/self/mountinfo: 1258 1241 0:189 / /proc/acpi ro,relatime - tmpfs tmpfs ro,inode64 >> /proc/self/mountinfo: 1259 1241 0:185 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64 >> /proc/self/mountinfo: 1260 1241 0:185 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64 >> /proc/self/mountinfo: 1375 1241 0:185 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755,inode64 >> /proc/self/mountinfo: 1410 1241 0:190 / /proc/scsi ro,relatime - tmpfs tmpfs ro,inode64 >> >> The 'fix' that Christian has suggested is out of the scope of the test >> because afaiu it should be done by the container management (before >> spawning the process that trigger the glibc testcase). > > So there's a few things to consider. What I wrote in that bug report > applies to unprivileged containers only - which is what the > systemd-nspawn bug report was about. If you're using a privileged > container things are different. So you need to figure that out first. > > (1) unprivileged container > If this is an unprivileged container then you can test whether my > fix works by trying: > > umount /proc/kcore > > If that works then the mount isn't locked. So that means you could > reveal the underlying files. If that's the case then within the > container manager you can do: > > mount -t proc proc /run/host/proc Thanks for the explanation, but I still not really sure what this test still in my environment (basically a docker container with CAP_SYS_ADMIN plus apparmor disabled). It does not seem to be this case, the running within the container and adding a explicit umount on the test: $ strace -e umount2 -f misc/tst-ttyname-namespace --direct umount2("/proc/kcore", 0) = -1 EINVAL (Invalid argument) > > (2) privileged container > (i) If this is a privileged container your container manager might > simply have dropped CAP_SYS_ADMIN so you can neither mount or > umount. > (ii) The files and directories covering various proc files and > directories are protected by an LSM profile making it > impossible to unmount them. For this to be meaningful you would > need to be restricted either from creating namespaces - which > doesn't seem to be the case - or the LSM profile would have to > restrict you from mounting procfs. > > If you happen to have bpftrace you can test this quickly by: > > # This will report the return value of security_sb_mount(). > sudo bpftrace -e 'kretprobe:security_sb_mount { printf("returned: %d\n", retval); }' > > and then try: > > mount -t proc proc /mnt > > and see whether you get an errno. I am not really seeing any issue here: # bpftrace -e 'kretprobe:security_sb_mount { printf("returned: %d\n", retval); }' Attaching 1 probe... returned: 0 returned: 0 returned: 0 returned: 0 returned: 0 While running: $ misc/tst-ttyname-namespace info: entering chroot 2 info: testcase: basic smoketest info: ttyname: PASS {name="/dev/pts/2", errno=0} info: ttyname_r: PASS {name="/dev/pts/2", ret=0, errno=0} error: ../sysdeps/unix/sysv/linux/tst-ttyname-namespace.c:97: mount ("proc", "/proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) == 0: Operation not permitted This is being really time consuming to figure out the underlying issue, so I really think we should move the test to make it as UNSUPPORTED if the procfs mount fails; it really does not seem to be a test failure but rather system configuration issue (which fails on the UNSUPPORTED case).
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index 594a5dc53e..23a84cf225 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -224,7 +224,8 @@ tests += \ tst-sysvshm-linux \ tst-tgkill \ tst-timerfd \ - tst-ttyname \ + tst-ttyname-direct \ + tst-ttyname-namespace \ # tests # process_madvise requires CAP_SYS_ADMIN. diff --git a/sysdeps/unix/sysv/linux/tst-ttyname.c b/sysdeps/unix/sysv/linux/tst-ttyname-common.c similarity index 68% rename from sysdeps/unix/sysv/linux/tst-ttyname.c rename to sysdeps/unix/sysv/linux/tst-ttyname-common.c index ef55665fbe..3f6d8ee944 100644 --- a/sysdeps/unix/sysv/linux/tst-ttyname.c +++ b/sysdeps/unix/sysv/linux/tst-ttyname-common.c @@ -1,4 +1,5 @@ -/* Copyright (C) 2017-2023 Free Software Foundation, Inc. +/* Common definitions for ttyname tests. + Copyright (C) 2017-2023 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -19,19 +20,16 @@ #include <errno.h> #include <fcntl.h> #include <limits.h> -#include <sched.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mount.h> -#include <sys/prctl.h> -#include <sys/stat.h> -#include <sys/wait.h> #include <sys/resource.h> +#include <sys/stat.h> #include <unistd.h> #include <support/check.h> -#include <support/namespace.h> #include <support/support.h> #include <support/temp_file.h> #include <support/test-driver.h> @@ -266,187 +264,6 @@ adjust_file_limit (const char *pty) } } -/* These chroot setup functions put the TTY at at "/console" (where it - won't be found by ttyname), and create "/dev/console" as an - ordinary file. This way, it's easier to write test-cases that - expect ttyname to fail; test-cases that expect it to succeed need - to explicitly remount it at "/dev/console". */ - -static int -do_in_chroot_1 (int (*cb)(const char *, int)) -{ - printf ("info: entering chroot 1\n"); - - /* Open the PTS that we'll be testing on. */ - int master; - char *slavename; - master = posix_openpt (O_RDWR|O_NOCTTY|O_NONBLOCK); - if (master < 0) - { - if (errno == ENOENT) - FAIL_UNSUPPORTED ("posix_openpt: %m"); - else - FAIL_EXIT1 ("posix_openpt: %m"); - } - VERIFY ((slavename = ptsname (master))); - VERIFY (unlockpt (master) == 0); - if (strncmp (slavename, "/dev/pts/", 9) != 0) - FAIL_UNSUPPORTED ("slave pseudo-terminal is not under /dev/pts/: %s", - slavename); - adjust_file_limit (slavename); - int slave = xopen (slavename, O_RDWR, 0); - if (!doit (slave, "basic smoketest", - (struct result_r){.name=slavename, .ret=0, .err=0})) - return 1; - - pid_t pid = xfork (); - if (pid == 0) - { - xclose (master); - - if (!support_enter_mount_namespace ()) - FAIL_UNSUPPORTED ("could not enter new mount namespace"); - - VERIFY (mount ("tmpfs", chrootdir, "tmpfs", 0, "mode=755") == 0); - VERIFY (chdir (chrootdir) == 0); - - xmkdir ("proc", 0755); - xmkdir ("dev", 0755); - xmkdir ("dev/pts", 0755); - - VERIFY (mount ("/proc", "proc", NULL, MS_BIND|MS_REC, NULL) == 0); - VERIFY (mount ("devpts", "dev/pts", "devpts", - MS_NOSUID|MS_NOEXEC, - "newinstance,ptmxmode=0666,mode=620") == 0); - VERIFY (symlink ("pts/ptmx", "dev/ptmx") == 0); - - touch ("console", 0); - touch ("dev/console", 0); - VERIFY (mount (slavename, "console", NULL, MS_BIND, NULL) == 0); - - xchroot ("."); - - char *linkname = xasprintf ("/proc/self/fd/%d", slave); - char *target = proc_fd_readlink (linkname); - VERIFY (strcmp (target, slavename) == 0); - free (linkname); - - _exit (cb (slavename, slave)); - } - int status; - xwaitpid (pid, &status, 0); - VERIFY (WIFEXITED (status)); - xclose (master); - xclose (slave); - return WEXITSTATUS (status); -} - -static int -do_in_chroot_2 (int (*cb)(const char *, int)) -{ - printf ("info: entering chroot 2\n"); - - int pid_pipe[2]; - xpipe (pid_pipe); - int exit_pipe[2]; - xpipe (exit_pipe); - - /* Open the PTS that we'll be testing on. */ - int master; - char *slavename; - VERIFY ((master = posix_openpt (O_RDWR|O_NOCTTY|O_NONBLOCK)) >= 0); - VERIFY ((slavename = ptsname (master))); - VERIFY (unlockpt (master) == 0); - if (strncmp (slavename, "/dev/pts/", 9) != 0) - FAIL_UNSUPPORTED ("slave pseudo-terminal is not under /dev/pts/: %s", - slavename); - adjust_file_limit (slavename); - /* wait until in a new mount ns to open the slave */ - - /* enable `wait`ing on grandchildren */ - VERIFY (prctl (PR_SET_CHILD_SUBREAPER, 1) == 0); - - pid_t pid = xfork (); /* outer child */ - if (pid == 0) - { - xclose (master); - xclose (pid_pipe[0]); - xclose (exit_pipe[1]); - - if (!support_enter_mount_namespace ()) - FAIL_UNSUPPORTED ("could not enter new mount namespace"); - - int slave = xopen (slavename, O_RDWR, 0); - if (!doit (slave, "basic smoketest", - (struct result_r){.name=slavename, .ret=0, .err=0})) - _exit (1); - - VERIFY (mount ("tmpfs", chrootdir, "tmpfs", 0, "mode=755") == 0); - VERIFY (chdir (chrootdir) == 0); - - xmkdir ("proc", 0755); - xmkdir ("dev", 0755); - xmkdir ("dev/pts", 0755); - - VERIFY (mount ("devpts", "dev/pts", "devpts", - MS_NOSUID|MS_NOEXEC, - "newinstance,ptmxmode=0666,mode=620") == 0); - VERIFY (symlink ("pts/ptmx", "dev/ptmx") == 0); - - touch ("console", 0); - touch ("dev/console", 0); - VERIFY (mount (slavename, "console", NULL, MS_BIND, NULL) == 0); - - xchroot ("."); - - if (unshare (CLONE_NEWNS | CLONE_NEWPID) < 0) - FAIL_UNSUPPORTED ("could not enter new PID namespace"); - pid = xfork (); /* inner child */ - if (pid == 0) - { - xclose (pid_pipe[1]); - - /* wait until the outer child has exited */ - char c; - VERIFY (read (exit_pipe[0], &c, 1) == 0); - xclose (exit_pipe[0]); - - VERIFY (mount ("proc", "/proc", "proc", - MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) == 0); - - char *linkname = xasprintf ("/proc/self/fd/%d", slave); - char *target = proc_fd_readlink (linkname); - VERIFY (strcmp (target, strrchr (slavename, '/')) == 0); - free (linkname); - - _exit (cb (slavename, slave)); - } - xwrite (pid_pipe[1], &pid, sizeof pid); - _exit (0); - } - xclose (pid_pipe[1]); - xclose (exit_pipe[0]); - xclose (exit_pipe[1]); - - /* wait for the outer child */ - int status; - xwaitpid (pid, &status, 0); - VERIFY (WIFEXITED (status)); - int ret = WEXITSTATUS (status); - if (ret != 0) - return ret; - - /* set 'pid' to the inner child */ - VERIFY (read (pid_pipe[0], &pid, sizeof pid) == sizeof pid); - xclose (pid_pipe[0]); - - /* wait for the inner child */ - xwaitpid (pid, &status, 0); - VERIFY (WIFEXITED (status)); - xclose (master); - return WEXITSTATUS (status); -} - /* main test */ static int @@ -597,20 +414,3 @@ run_chroot_tests (const char *slavename, int slave) return ok ? 0 : 1; } -static int -do_test (void) -{ - support_become_root (); - - int ret1 = do_in_chroot_1 (run_chroot_tests); - if (ret1 == EXIT_UNSUPPORTED) - return ret1; - - int ret2 = do_in_chroot_2 (run_chroot_tests); - if (ret2 == EXIT_UNSUPPORTED) - return ret2; - - return ret1 | ret2; -} - -#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/tst-ttyname-direct.c b/sysdeps/unix/sysv/linux/tst-ttyname-direct.c new file mode 100644 index 0000000000..04601777d6 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-ttyname-direct.c @@ -0,0 +1,110 @@ +/* Copyright (C) 2017-2023 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <https://www.gnu.org/licenses/>. */ + +#include <sched.h> +#include <sys/prctl.h> + +#include <support/namespace.h> + +#include "tst-ttyname-common.c" + +/* These chroot setup functions put the TTY at at "/console" (where it + won't be found by ttyname), and create "/dev/console" as an + ordinary file. This way, it's easier to write test-cases that + expect ttyname to fail; test-cases that expect it to succeed need + to explicitly remount it at "/dev/console". */ + +static int +do_in_chroot_1 (int (*cb)(const char *, int)) +{ + printf ("info: entering chroot 1\n"); + + /* Open the PTS that we'll be testing on. */ + int master; + char *slavename; + master = posix_openpt (O_RDWR|O_NOCTTY|O_NONBLOCK); + if (master < 0) + { + if (errno == ENOENT) + FAIL_UNSUPPORTED ("posix_openpt: %m"); + else + FAIL_EXIT1 ("posix_openpt: %m"); + } + VERIFY ((slavename = ptsname (master))); + VERIFY (unlockpt (master) == 0); + if (strncmp (slavename, "/dev/pts/", 9) != 0) + FAIL_UNSUPPORTED ("slave pseudo-terminal is not under /dev/pts/: %s", + slavename); + adjust_file_limit (slavename); + int slave = xopen (slavename, O_RDWR, 0); + if (!doit (slave, "basic smoketest", + (struct result_r){.name=slavename, .ret=0, .err=0})) + return 1; + + pid_t pid = xfork (); + if (pid == 0) + { + xclose (master); + + if (!support_enter_mount_namespace ()) + FAIL_UNSUPPORTED ("could not enter new mount namespace"); + + VERIFY (mount ("tmpfs", chrootdir, "tmpfs", 0, "mode=755") == 0); + VERIFY (chdir (chrootdir) == 0); + + xmkdir ("proc", 0755); + xmkdir ("dev", 0755); + xmkdir ("dev/pts", 0755); + + VERIFY (mount ("/proc", "proc", NULL, MS_BIND|MS_REC, NULL) == 0); + VERIFY (mount ("devpts", "dev/pts", "devpts", + MS_NOSUID|MS_NOEXEC, + "newinstance,ptmxmode=0666,mode=620") == 0); + VERIFY (symlink ("pts/ptmx", "dev/ptmx") == 0); + + touch ("console", 0); + touch ("dev/console", 0); + VERIFY (mount (slavename, "console", NULL, MS_BIND, NULL) == 0); + + xchroot ("."); + + char *linkname = xasprintf ("/proc/self/fd/%d", slave); + char *target = proc_fd_readlink (linkname); + VERIFY (strcmp (target, slavename) == 0); + free (linkname); + + _exit (cb (slavename, slave)); + } + int status; + xwaitpid (pid, &status, 0); + VERIFY (WIFEXITED (status)); + xclose (master); + xclose (slave); + return WEXITSTATUS (status); +} + +static int +do_test (void) +{ + support_become_root (); + + do_in_chroot_1 (run_chroot_tests); + + return 0; +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/tst-ttyname-namespace.c b/sysdeps/unix/sysv/linux/tst-ttyname-namespace.c new file mode 100644 index 0000000000..7b74258c10 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-ttyname-namespace.c @@ -0,0 +1,147 @@ +/* Tests for ttyname/ttyname_r with namespaces. + Copyright (C) 2017-2023 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <https://www.gnu.org/licenses/>. */ + +#include <sched.h> +#include <sys/prctl.h> +#include <sys/wait.h> + +#include <support/namespace.h> + +#include "tst-ttyname-common.c" + +static int +do_in_chroot_2 (int (*cb)(const char *, int)) +{ + printf ("info: entering chroot 2\n"); + + int pid_pipe[2]; + xpipe (pid_pipe); + int exit_pipe[2]; + xpipe (exit_pipe); + + /* Open the PTS that we'll be testing on. */ + int master; + char *slavename; + VERIFY ((master = posix_openpt (O_RDWR|O_NOCTTY|O_NONBLOCK)) >= 0); + VERIFY ((slavename = ptsname (master))); + VERIFY (unlockpt (master) == 0); + if (strncmp (slavename, "/dev/pts/", 9) != 0) + FAIL_UNSUPPORTED ("slave pseudo-terminal is not under /dev/pts/: %s", + slavename); + adjust_file_limit (slavename); + /* wait until in a new mount ns to open the slave */ + + /* enable `wait`ing on grandchildren */ + VERIFY (prctl (PR_SET_CHILD_SUBREAPER, 1) == 0); + + pid_t pid = xfork (); /* outer child */ + if (pid == 0) + { + xclose (master); + xclose (pid_pipe[0]); + xclose (exit_pipe[1]); + + if (!support_enter_mount_namespace ()) + FAIL_UNSUPPORTED ("could not enter new mount namespace"); + + int slave = xopen (slavename, O_RDWR, 0); + if (!doit (slave, "basic smoketest", + (struct result_r){.name=slavename, .ret=0, .err=0})) + _exit (1); + + VERIFY (mount ("tmpfs", chrootdir, "tmpfs", 0, "mode=755") == 0); + VERIFY (chdir (chrootdir) == 0); + + xmkdir ("proc", 0755); + xmkdir ("dev", 0755); + xmkdir ("dev/pts", 0755); + + VERIFY (mount ("devpts", "dev/pts", "devpts", + MS_NOSUID|MS_NOEXEC, + "newinstance,ptmxmode=0666,mode=620") == 0); + VERIFY (symlink ("pts/ptmx", "dev/ptmx") == 0); + + touch ("console", 0); + touch ("dev/console", 0); + VERIFY (mount (slavename, "console", NULL, MS_BIND, NULL) == 0); + + xchroot ("."); + + if (unshare (CLONE_NEWNS | CLONE_NEWPID) < 0) + FAIL_UNSUPPORTED ("could not enter new PID namespace"); + pid = xfork (); /* inner child */ + if (pid == 0) + { + xclose (pid_pipe[1]); + + /* wait until the outer child has exited */ + char c; + VERIFY (read (exit_pipe[0], &c, 1) == 0); + xclose (exit_pipe[0]); + + if (mount ("proc", "/proc", "proc", + MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0) + { + /* This happens if we're trying to create a nested container, + like if the build is running under podman, and we lack + priviledges. */ + if (errno == EPERM) + _exit (EXIT_UNSUPPORTED); + else + _exit (EXIT_FAILURE); + } + + char *linkname = xasprintf ("/proc/self/fd/%d", slave); + char *target = proc_fd_readlink (linkname); + VERIFY (strcmp (target, strrchr (slavename, '/')) == 0); + free (linkname); + + _exit (cb (slavename, slave)); + } + + int status; + xwaitpid (pid, &status, 0); + _exit (WEXITSTATUS (status)); + } + xclose (pid_pipe[1]); + xclose (exit_pipe[0]); + xclose (exit_pipe[1]); + + /* wait for the outer child */ + int status; + xwaitpid (pid, &status, 0); + VERIFY (WIFEXITED (status)); + int ret = WEXITSTATUS (status); + if (ret != 0) + FAIL_UNSUPPORTED ("unable to mount /proc on inner child process"); + xclose (master); + + return 0; +} + +static int +do_test (void) +{ + support_become_root (); + + do_in_chroot_2 (run_chroot_tests); + + return 0; +} + +#include <support/test-driver.c>