c641a2
commit 2fe64148a81f0d78050c302f34a6853d21f7cae4
c641a2
Author: DJ Delorie <dj@redhat.com>
c641a2
Date:   Mon Mar 28 23:53:33 2022 -0400
c641a2
c641a2
    Allow for unpriviledged nested containers
c641a2
    
c641a2
    If the build itself is run in a container, we may not be able to
c641a2
    fully set up a nested container for test-container testing.
c641a2
    Notably is the mounting of /proc, since it's critical that it
c641a2
    be mounted from within the same PID namespace as its users, and
c641a2
    thus cannot be bind mounted from outside the container like other
c641a2
    mounts.
c641a2
    
c641a2
    This patch defaults to using the parent's PID namespace instead of
c641a2
    creating a new one, as this is more likely to be allowed.
c641a2
    
c641a2
    If the test needs an isolated PID namespace, it should add the "pidns"
c641a2
    command to its init script.
c641a2
    
c641a2
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
c641a2
c641a2
Conflicts:
c641a2
	nss/tst-reload2.c
c641a2
          (not in RHEL-8)
c641a2
	support/Makefile
c641a2
          (RHEL-8 missing some routines in libsupport-routines)
c641a2
c641a2
diff --git a/elf/tst-pldd.c b/elf/tst-pldd.c
c641a2
index f381cb0fa7e6b93d..45ac033a0f897088 100644
c641a2
--- a/elf/tst-pldd.c
c641a2
+++ b/elf/tst-pldd.c
c641a2
@@ -85,6 +85,8 @@ in_str_list (const char *libname, const char *const strlist[])
c641a2
 static int
c641a2
 do_test (void)
c641a2
 {
c641a2
+  support_need_proc ("needs /proc/sys/kernel/yama/ptrace_scope and /proc/$child");
c641a2
+
c641a2
   /* Check if our subprocess can be debugged with ptrace.  */
c641a2
   {
c641a2
     int ptrace_scope = support_ptrace_scope ();
c641a2
diff --git a/nptl/tst-pthread-getattr.c b/nptl/tst-pthread-getattr.c
c641a2
index 273b6073abe9cb60..f1c0b39f3a27724c 100644
c641a2
--- a/nptl/tst-pthread-getattr.c
c641a2
+++ b/nptl/tst-pthread-getattr.c
c641a2
@@ -28,6 +28,8 @@
c641a2
 #include <unistd.h>
c641a2
 #include <inttypes.h>
c641a2
 
c641a2
+#include <support/support.h>
c641a2
+
c641a2
 /* There is an obscure bug in the kernel due to which RLIMIT_STACK is sometimes
c641a2
    returned as unlimited when it is not, which may cause this test to fail.
c641a2
    There is also the other case where RLIMIT_STACK is intentionally set as
c641a2
@@ -152,6 +154,8 @@ check_stack_top (void)
c641a2
 static int
c641a2
 do_test (void)
c641a2
 {
c641a2
+  support_need_proc ("Reads /proc/self/maps to get stack size.");
c641a2
+
c641a2
   pagesize = sysconf (_SC_PAGESIZE);
c641a2
   return check_stack_top ();
c641a2
 }
c641a2
diff --git a/support/Makefile b/support/Makefile
c641a2
index 636d69c4f8e7e139..e184fccbe7d2310c 100644
c641a2
--- a/support/Makefile
c641a2
+++ b/support/Makefile
c641a2
@@ -59,6 +59,7 @@ libsupport-routines = \
c641a2
   support_format_hostent \
c641a2
   support_format_netent \
c641a2
   support_isolate_in_subprocess \
c641a2
+  support_need_proc \
c641a2
   support_process_state \
c641a2
   support_ptrace \
c641a2
   support_openpty \
c641a2
diff --git a/support/support.h b/support/support.h
c641a2
index 96833bd4e992e6d3..1466eb29f840fa59 100644
c641a2
--- a/support/support.h
c641a2
+++ b/support/support.h
c641a2
@@ -81,6 +81,11 @@ char *support_quote_string (const char *);
c641a2
    regular file open for writing, and initially empty.  */
c641a2
 int support_descriptor_supports_holes (int fd);
c641a2
 
c641a2
+/* Predicates that a test requires a working /proc filesystem.  This
c641a2
+   call will exit with UNSUPPORTED if /proc is not available, printing
c641a2
+   WHY_MSG as part of the diagnostic.  */
c641a2
+void support_need_proc (const char *why_msg);
c641a2
+
c641a2
 /* Error-checking wrapper functions which terminate the process on
c641a2
    error.  */
c641a2
 
c641a2
diff --git a/support/support_need_proc.c b/support/support_need_proc.c
c641a2
new file mode 100644
c641a2
index 0000000000000000..9b4eab7539b2d6c3
c641a2
--- /dev/null
c641a2
+++ b/support/support_need_proc.c
c641a2
@@ -0,0 +1,35 @@
c641a2
+/* Indicate that a test requires a working /proc.
c641a2
+   Copyright (C) 2022 Free Software Foundation, Inc.
c641a2
+   This file is part of the GNU C Library.
c641a2
+
c641a2
+   The GNU C Library is free software; you can redistribute it and/or
c641a2
+   modify it under the terms of the GNU Lesser General Public
c641a2
+   License as published by the Free Software Foundation; either
c641a2
+   version 2.1 of the License, or (at your option) any later version.
c641a2
+
c641a2
+   The GNU C Library is distributed in the hope that it will be useful,
c641a2
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
c641a2
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
c641a2
+   Lesser General Public License for more details.
c641a2
+
c641a2
+   You should have received a copy of the GNU Lesser General Public
c641a2
+   License along with the GNU C Library; if not, see
c641a2
+   <https://www.gnu.org/licenses/>.  */
c641a2
+
c641a2
+#include <unistd.h>
c641a2
+#include <support/check.h>
c641a2
+#include <support/support.h>
c641a2
+
c641a2
+/* We test for /proc/self/maps since that's one of the files that one
c641a2
+   of our tests actually uses, but the general idea is if Linux's
c641a2
+   /proc/ (procfs) filesystem is mounted.  If not, the process exits
c641a2
+   with an UNSUPPORTED result code.  */
c641a2
+
c641a2
+void
c641a2
+support_need_proc (const char *why_msg)
c641a2
+{
c641a2
+#ifdef __linux__
c641a2
+  if (access ("/proc/self/maps", R_OK))
c641a2
+    FAIL_UNSUPPORTED ("/proc is not available, %s", why_msg);
c641a2
+#endif
c641a2
+}
c641a2
diff --git a/support/test-container.c b/support/test-container.c
c641a2
index 9975c8cb7bc9a955..2bce4db841ff7668 100644
c641a2
--- a/support/test-container.c
c641a2
+++ b/support/test-container.c
c641a2
@@ -95,6 +95,7 @@ int verbose = 0;
c641a2
    * mytest.root/mytest.script has a list of "commands" to run:
c641a2
        syntax:
c641a2
          # comment
c641a2
+	 pidns <comment>
c641a2
          su
c641a2
          mv FILE FILE
c641a2
 	 cp FILE FILE
c641a2
@@ -120,6 +121,8 @@ int verbose = 0;
c641a2
 
c641a2
        details:
c641a2
          - '#': A comment.
c641a2
+	 - 'pidns': Require a separate PID namespace, prints comment if it can't
c641a2
+	    (default is a shared pid namespace)
c641a2
          - 'su': Enables running test as root in the container.
c641a2
          - 'mv': A minimal move files command.
c641a2
          - 'cp': A minimal copy files command.
c641a2
@@ -143,7 +146,7 @@ int verbose = 0;
c641a2
    * Simple, easy to review code (i.e. prefer simple naive code over
c641a2
      complex efficient code)
c641a2
 
c641a2
-   * The current implementation ist parallel-make-safe, but only in
c641a2
+   * The current implementation is parallel-make-safe, but only in
c641a2
      that it uses a lock to prevent parallel access to the testroot.  */
c641a2
 
c641a2
 
c641a2
@@ -222,11 +225,37 @@ concat (const char *str, ...)
c641a2
   return bufs[n];
c641a2
 }
c641a2
 
c641a2
+/* Like the above, but put spaces between words.  Caller frees.  */
c641a2
+static char *
c641a2
+concat_words (char **words, int num_words)
c641a2
+{
c641a2
+  int len = 0;
c641a2
+  int i;
c641a2
+  char *rv, *p;
c641a2
+
c641a2
+  for (i = 0; i < num_words; i ++)
c641a2
+    {
c641a2
+      len += strlen (words[i]);
c641a2
+      len ++;
c641a2
+    }
c641a2
+
c641a2
+  p = rv = (char *) xmalloc (len);
c641a2
+
c641a2
+  for (i = 0; i < num_words; i ++)
c641a2
+    {
c641a2
+      if (i > 0)
c641a2
+	p = stpcpy (p, " ");
c641a2
+      p = stpcpy (p, words[i]);
c641a2
+    }
c641a2
+
c641a2
+  return rv;
c641a2
+}
c641a2
+
c641a2
 /* Try to mount SRC onto DEST.  */
c641a2
 static void
c641a2
 trymount (const char *src, const char *dest)
c641a2
 {
c641a2
-  if (mount (src, dest, "", MS_BIND, NULL) < 0)
c641a2
+  if (mount (src, dest, "", MS_BIND | MS_REC, NULL) < 0)
c641a2
     FAIL_EXIT1 ("can't mount %s onto %s\n", src, dest);
c641a2
 }
c641a2
 
c641a2
@@ -709,6 +738,9 @@ main (int argc, char **argv)
c641a2
   gid_t original_gid;
c641a2
   /* If set, the test runs as root instead of the user running the testsuite.  */
c641a2
   int be_su = 0;
c641a2
+  int require_pidns = 0;
c641a2
+  const char *pidns_comment = NULL;
c641a2
+  int do_proc_mounts = 0;
c641a2
   int UMAP;
c641a2
   int GMAP;
c641a2
   /* Used for "%lld %lld 1" so need not be large.  */
c641a2
@@ -991,6 +1023,12 @@ main (int argc, char **argv)
c641a2
 	      {
c641a2
 		be_su = 1;
c641a2
 	      }
c641a2
+	    else if (nt >= 1 && strcmp (the_words[0], "pidns") == 0)
c641a2
+	      {
c641a2
+		require_pidns = 1;
c641a2
+		if (nt > 1)
c641a2
+		  pidns_comment = concat_words (the_words + 1, nt - 1);
c641a2
+	      }
c641a2
 	    else if (nt == 3 && strcmp (the_words[0], "mkdirp") == 0)
c641a2
 	      {
c641a2
 		long int m;
c641a2
@@ -1048,7 +1086,8 @@ main (int argc, char **argv)
c641a2
 
c641a2
 #ifdef CLONE_NEWNS
c641a2
   /* The unshare here gives us our own spaces and capabilities.  */
c641a2
-  if (unshare (CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS) < 0)
c641a2
+  if (unshare (CLONE_NEWUSER | CLONE_NEWNS
c641a2
+	       | (require_pidns ? CLONE_NEWPID : 0)) < 0)
c641a2
     {
c641a2
       /* Older kernels may not support all the options, or security
c641a2
 	 policy may block this call.  */
c641a2
@@ -1059,6 +1098,11 @@ main (int argc, char **argv)
c641a2
 	    check_for_unshare_hints ();
c641a2
 	  FAIL_UNSUPPORTED ("unable to unshare user/fs: %s", strerror (saved_errno));
c641a2
 	}
c641a2
+      /* We're about to exit anyway, it's "safe" to call unshare again
c641a2
+	 just to see if the CLONE_NEWPID caused the error.  */
c641a2
+      else if (require_pidns && unshare (CLONE_NEWUSER | CLONE_NEWNS) >= 0)
c641a2
+	FAIL_EXIT1 ("unable to unshare pid ns: %s : %s", strerror (errno),
c641a2
+		    pidns_comment ? pidns_comment : "required by test");
c641a2
       else
c641a2
 	FAIL_EXIT1 ("unable to unshare user/fs: %s", strerror (errno));
c641a2
     }
c641a2
@@ -1074,6 +1118,15 @@ main (int argc, char **argv)
c641a2
   trymount (support_srcdir_root, new_srcdir_path);
c641a2
   trymount (support_objdir_root, new_objdir_path);
c641a2
 
c641a2
+  /* It may not be possible to mount /proc directly.  */
c641a2
+  if (! require_pidns)
c641a2
+  {
c641a2
+    char *new_proc = concat (new_root_path, "/proc", NULL);
c641a2
+    xmkdirp (new_proc, 0755);
c641a2
+    trymount ("/proc", new_proc);
c641a2
+    do_proc_mounts = 1;
c641a2
+  }
c641a2
+
c641a2
   xmkdirp (concat (new_root_path, "/dev", NULL), 0755);
c641a2
   devmount (new_root_path, "null");
c641a2
   devmount (new_root_path, "zero");
c641a2
@@ -1136,42 +1189,60 @@ main (int argc, char **argv)
c641a2
 
c641a2
   maybe_xmkdir ("/tmp", 0755);
c641a2
 
c641a2
-  /* Now that we're pid 1 (effectively "root") we can mount /proc  */
c641a2
-  maybe_xmkdir ("/proc", 0777);
c641a2
-  if (mount ("proc", "/proc", "proc", 0, NULL) < 0)
c641a2
-    FAIL_EXIT1 ("Unable to mount /proc: ");
c641a2
-
c641a2
-  /* We map our original UID to the same UID in the container so we
c641a2
-     can own our own files normally.  */
c641a2
-  UMAP = open ("/proc/self/uid_map", O_WRONLY);
c641a2
-  if (UMAP < 0)
c641a2
-    FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
c641a2
-
c641a2
-  sprintf (tmp, "%lld %lld 1\n",
c641a2
-	   (long long) (be_su ? 0 : original_uid), (long long) original_uid);
c641a2
-  write (UMAP, tmp, strlen (tmp));
c641a2
-  xclose (UMAP);
c641a2
-
c641a2
-  /* We must disable setgroups () before we can map our groups, else we
c641a2
-     get EPERM.  */
c641a2
-  GMAP = open ("/proc/self/setgroups", O_WRONLY);
c641a2
-  if (GMAP >= 0)
c641a2
+  if (require_pidns)
c641a2
     {
c641a2
-      /* We support kernels old enough to not have this.  */
c641a2
-      write (GMAP, "deny\n", 5);
c641a2
-      xclose (GMAP);
c641a2
+      /* Now that we're pid 1 (effectively "root") we can mount /proc  */
c641a2
+      maybe_xmkdir ("/proc", 0777);
c641a2
+      if (mount ("proc", "/proc", "proc", 0, NULL) != 0)
c641a2
+	{
c641a2
+	  /* This happens if we're trying to create a nested container,
c641a2
+	     like if the build is running under podman, and we lack
c641a2
+	     priviledges.
c641a2
+
c641a2
+	     Ideally we would WARN here, but that would just add noise to
c641a2
+	     *every* test-container test, and the ones that care should
c641a2
+	     have their own relevent diagnostics.
c641a2
+
c641a2
+	     FAIL_EXIT1 ("Unable to mount /proc: ");  */
c641a2
+	}
c641a2
+      else
c641a2
+	do_proc_mounts = 1;
c641a2
     }
c641a2
 
c641a2
-  /* We map our original GID to the same GID in the container so we
c641a2
-     can own our own files normally.  */
c641a2
-  GMAP = open ("/proc/self/gid_map", O_WRONLY);
c641a2
-  if (GMAP < 0)
c641a2
-    FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
c641a2
+  if (do_proc_mounts)
c641a2
+    {
c641a2
+      /* We map our original UID to the same UID in the container so we
c641a2
+	 can own our own files normally.  */
c641a2
+      UMAP = open ("/proc/self/uid_map", O_WRONLY);
c641a2
+      if (UMAP < 0)
c641a2
+	FAIL_EXIT1 ("can't write to /proc/self/uid_map\n");
c641a2
+
c641a2
+      sprintf (tmp, "%lld %lld 1\n",
c641a2
+	       (long long) (be_su ? 0 : original_uid), (long long) original_uid);
c641a2
+      write (UMAP, tmp, strlen (tmp));
c641a2
+      xclose (UMAP);
c641a2
+
c641a2
+      /* We must disable setgroups () before we can map our groups, else we
c641a2
+	 get EPERM.  */
c641a2
+      GMAP = open ("/proc/self/setgroups", O_WRONLY);
c641a2
+      if (GMAP >= 0)
c641a2
+	{
c641a2
+	  /* We support kernels old enough to not have this.  */
c641a2
+	  write (GMAP, "deny\n", 5);
c641a2
+	  xclose (GMAP);
c641a2
+	}
c641a2
 
c641a2
-  sprintf (tmp, "%lld %lld 1\n",
c641a2
-	   (long long) (be_su ? 0 : original_gid), (long long) original_gid);
c641a2
-  write (GMAP, tmp, strlen (tmp));
c641a2
-  xclose (GMAP);
c641a2
+      /* We map our original GID to the same GID in the container so we
c641a2
+	 can own our own files normally.  */
c641a2
+      GMAP = open ("/proc/self/gid_map", O_WRONLY);
c641a2
+      if (GMAP < 0)
c641a2
+	FAIL_EXIT1 ("can't write to /proc/self/gid_map\n");
c641a2
+
c641a2
+      sprintf (tmp, "%lld %lld 1\n",
c641a2
+	       (long long) (be_su ? 0 : original_gid), (long long) original_gid);
c641a2
+      write (GMAP, tmp, strlen (tmp));
c641a2
+      xclose (GMAP);
c641a2
+    }
c641a2
 
c641a2
   if (change_cwd)
c641a2
     {