From patchwork Mon Jun 5 18:32:43 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 70614 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 745703855583 for ; Mon, 5 Jun 2023 18:33:13 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 745703855583 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1685989993; bh=F8Gc1xdXtNaElOU7ll9c9UB6O1tpzbeUWHXhUZuKQCw=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=fXUPX0eVEW/Eb29/IekspXfjWl4PqrFeuB5bDMqHs6/RkiwbxEKNMmslPpbGG9jR2 rSvW7el6bMnPcNwFtdi1WG/wITIYqbRC+qrIidoEQLPbHR2T/3S29qkwh/TePAv2z5 02g6TTfW6dIWp51mWrU1RBFMLnm3XYTBFmOB4k2I= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-oa1-x2e.google.com (mail-oa1-x2e.google.com [IPv6:2001:4860:4864:20::2e]) by sourceware.org (Postfix) with ESMTPS id 73F423858C30 for ; Mon, 5 Jun 2023 18:32:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 73F423858C30 Received: by mail-oa1-x2e.google.com with SMTP id 586e51a60fabf-19a427d7b57so2895603fac.2 for ; Mon, 05 Jun 2023 11:32:49 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1685989968; x=1688581968; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=F8Gc1xdXtNaElOU7ll9c9UB6O1tpzbeUWHXhUZuKQCw=; b=FOotzLLpPWG5SEvegLSn9d/SMVuceDxfc4UhmXyFEA+cGleJwJeUaLw/o7ZDM8Nrjb ETUdpuXDzUhDX+YCZi5lojpq/+xpGqNQ0havO+wLMSI1jmnf9fUeyPNvu3Au/6VoJ06P +kF6OHj0wBZc9gdmKiFkGrMrvaa5+mhv4mRhN9cMaSmlpdkntWdXEKLNEg8MxXKXLAFQ 9Y1yg31Mso1lQQWSuLTqBnEpzbZlfFgj6NOnXufvXn1iBNqDBgjfyMS+WKZeec/hTwZu 14Fr4iaoJXsWce7eCKSAE3wAPxBTvcL6EvRSZQlcvqOsIdI04Mk2reRTDnQcp1AI5nUm e5dw== X-Gm-Message-State: AC+VfDx8KMv+sEkITkPWyeVs5PCK0HcyPIa6/4wiqrBGXapbsb2yLzg/ MiViXTsd+t90a95hIV1lBt4xYxBCyUB4W7Ml/1ClOA== X-Google-Smtp-Source: ACHHUZ5YvkLj1RbpOiURB2OdAtiQO0d9B//gl++FCLXsEaUnupRpBYONV2+tNKkWMRn+R9Dmed2yQg== X-Received: by 2002:a05:6870:c809:b0:19f:add7:2cd4 with SMTP id ee9-20020a056870c80900b0019fadd72cd4mr470479oab.32.1685989967972; Mon, 05 Jun 2023 11:32:47 -0700 (PDT) Received: from mandiga.. ([2804:1b3:a7c1:fe49:fc67:669d:9bf8:5d9c]) by smtp.gmail.com with ESMTPSA id zh35-20020a0568716ba300b0017fea9c156esm3892549oab.18.2023.06.05.11.32.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 05 Jun 2023 11:32:47 -0700 (PDT) To: libc-alpha@sourceware.org, Florian Weimer Subject: [PATCH v2] linux: Split tst-ttyname Date: Mon, 5 Jun 2023 15:32:43 -0300 Message-Id: <20230605183243.3493759-1-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_SBL_CSS, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Adhemerval Zanella via Libc-alpha From: Adhemerval Zanella Netto Reply-To: Adhemerval Zanella Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" 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. Checked on x86_64-linux-gnu and aarch64-linux-gnu. --- sysdeps/unix/sysv/linux/Makefile | 3 +- .../{tst-ttyname.c => tst-ttyname-common.c} | 208 +----------------- sysdeps/unix/sysv/linux/tst-ttyname-direct.c | 110 +++++++++ .../unix/sysv/linux/tst-ttyname-namespace.c | 147 +++++++++++++ 4 files changed, 263 insertions(+), 205 deletions(-) rename sysdeps/unix/sysv/linux/{tst-ttyname.c => tst-ttyname-common.c} (68%) create mode 100644 sysdeps/unix/sysv/linux/tst-ttyname-direct.c create mode 100644 sysdeps/unix/sysv/linux/tst-ttyname-namespace.c 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 #include #include -#include +#include #include #include #include #include -#include -#include -#include #include +#include #include #include -#include #include #include #include @@ -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 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 . */ + +#include +#include + +#include + +#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 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 . */ + +#include +#include +#include + +#include + +#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