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