From c324e79864c9c3f7bafd1d2396ca35647670a343 Mon Sep 17 00:00:00 2001
From: Fergus Dall <sidereal@google.com>
Date: Thu, 27 May 2021 23:20:02 +1000
Subject: [PATCH v2 2/2] rtld: Add tests for new --no-default-paths option
To: libc-alpha@sourceware.org
Cc: chromeos-toolchain@google.com,
    clumptini@google.com,
    vapier@google.com

---
 elf/Makefile                      | 15 ++++-
 elf/tst-no-default-paths-dlinfo.c | 94 ++++++++++++++++++++++++++++++
 elf/tst-no-default-paths-dlopen.c | 96 +++++++++++++++++++++++++++++++
 elf/tst-no-default-paths-helper.c | 71 +++++++++++++++++++++++
 support/Makefile                  |  1 +
 support/support.h                 |  4 ++
 support/support_paths.c           |  6 ++
 7 files changed, 284 insertions(+), 3 deletions(-)
 create mode 100644 elf/tst-no-default-paths-dlinfo.c
 create mode 100644 elf/tst-no-default-paths-dlopen.c
 create mode 100644 elf/tst-no-default-paths-helper.c

diff --git a/elf/Makefile b/elf/Makefile
index 834ec858a8..f83fbd7a4a 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -223,7 +223,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 tst-single_threaded tst-single_threaded-pthread \
 	 tst-tls-ie tst-tls-ie-dlmopen argv0test \
 	 tst-glibc-hwcaps tst-glibc-hwcaps-prepend tst-glibc-hwcaps-mask \
-	 tst-tls20 tst-tls21 tst-dlmopen-dlerror
+	 tst-tls20 tst-tls21 tst-dlmopen-dlerror tst-no-default-paths-helper
 #	 reldep9
 tests-internal += loadtest unload unload2 circleload1 \
 	 neededtest neededtest2 neededtest3 neededtest4 \
@@ -231,7 +231,8 @@ tests-internal += loadtest unload unload2 circleload1 \
 	 tst-ptrguard1 tst-stackguard1 tst-libc_dlvsym \
 	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split
 tests-container += tst-pldd tst-dlopen-tlsmodid-container \
-  tst-dlopen-self-container tst-preload-pthread-libc
+  tst-dlopen-self-container tst-preload-pthread-libc \
+  tst-no-default-paths-dlopen tst-no-default-paths-dlinfo
 test-srcs = tst-pathopt
 selinux-enabled := $(shell cat /selinux/enforce 2> /dev/null)
 ifneq ($(selinux-enabled),1)
@@ -440,7 +441,8 @@ ifeq (yes,$(build-shared))
 ifeq ($(run-built-tests),yes)
 tests-special += $(objpfx)tst-pathopt.out $(objpfx)tst-rtld-load-self.out \
 		 $(objpfx)tst-rtld-preload.out $(objpfx)argv0test.out \
-		 $(objpfx)tst-rtld-help.out
+		 $(objpfx)tst-rtld-help.out \
+		 $(objpfx)tst-no-default-paths-helper.out
 endif
 tests-special += $(objpfx)check-textrel.out $(objpfx)check-execstack.out \
 		 $(objpfx)check-wx-segment.out \
@@ -1389,6 +1391,13 @@ $(objpfx)tst-dlopen-self-pie: $(libdl)
 $(objpfx)tst-dlopen-self-container: $(libdl)
 LDFLAGS-tst-dlopen-self-container += -Wl,-rpath,\$$ORIGIN
 
+$(objpfx)tst-no-default-paths-dlopen.out: $(objpfx)tst-no-default-paths-helper
+$(objpfx)tst-no-default-paths-dlinfo.out: $(objpfx)tst-no-default-paths-helper
+$(objpfx)tst-no-default-paths-helper.out:
+	touch $@
+	$(evaluate-test)
+$(objpfx)tst-no-default-paths-helper: $(libdl) $(objpfx)tst-no-default-paths-helper.o
+
 CFLAGS-ifuncmain1pic.c += $(pic-ccflag)
 CFLAGS-ifuncmain1picstatic.c += $(pic-ccflag)
 CFLAGS-ifuncmain1staticpic.c += $(pic-ccflag)
diff --git a/elf/tst-no-default-paths-dlinfo.c b/elf/tst-no-default-paths-dlinfo.c
new file mode 100644
index 0000000000..db075ab8e7
--- /dev/null
+++ b/elf/tst-no-default-paths-dlinfo.c
@@ -0,0 +1,94 @@
+/* Test that dlinfo respects --no-default-paths
+   Copyright (C) 2021 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; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <string.h>
+#include <sys/wait.h>
+#include <support/support.h>
+#include <support/subprocess.h>
+#include <support/xunistd.h>
+
+const char libs_dir[] = "/tmp/tst-no-default-paths-dlinfo";
+const char elf_dir[] = "/elf";
+const char marker_lib[] = "/libmarkermod1.so";
+const char libc[] = "/libc.so.6";
+const char libdl[] = "/libdl.so.2";
+const char helper_program[] = "/tst-no-default-paths-helper";
+
+// Link system lib into libs_dir so the child process can access it
+static void
+link_system_lib_to_dir (const char *system_lib)
+{
+  char lib_src[4096];
+  strcpy(lib_src, support_slibdir_prefix);
+  strcat(lib_src, system_lib);
+
+  char lib_dest[4096];
+  strcpy(lib_dest, libs_dir);
+  strcat(lib_dest, system_lib);
+
+  unlink(lib_dest);
+  xsymlink(lib_src, lib_dest);
+}
+
+// Link libmarker into libs_dir also
+static void
+link_libmarker (void)
+{
+  char marker_src[4096];
+  strcpy(marker_src, support_objdir_root);
+  strcat(marker_src, elf_dir);
+  strcat(marker_src, marker_lib);
+
+  char marker_dest[4096];
+  strcpy(marker_dest, libs_dir);
+  strcat(marker_dest, marker_lib);
+
+  unlink(marker_dest);
+  xsymlink(marker_src, marker_dest);
+}
+
+static int
+do_test (void)
+{
+  xmkdirp(libs_dir, 0755);
+
+  link_system_lib_to_dir(libc);
+  link_system_lib_to_dir(libdl);
+  link_libmarker();
+
+  char helper_path[4096];
+  strcpy(helper_path, support_objdir_root);
+  strcat(helper_path, elf_dir);
+  strcat(helper_path, helper_program);
+
+  char *const argv[] = {
+    strdup(support_container_elf_ldso_path),
+    strdup("--no-default-paths"),
+    strdup("--library-path"),
+    strdup(libs_dir),
+    helper_path,
+    strdup("dlinfo"),
+    NULL
+  };
+
+  int ret = support_subprogram_wait(argv[0], argv);
+
+  return WEXITSTATUS(ret);
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-no-default-paths-dlopen.c b/elf/tst-no-default-paths-dlopen.c
new file mode 100644
index 0000000000..acf2a48c07
--- /dev/null
+++ b/elf/tst-no-default-paths-dlopen.c
@@ -0,0 +1,96 @@
+/* Test that --no-default-paths doesn't search system dirs
+   Copyright (C) 2021 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; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <string.h>
+#include <sys/wait.h>
+#include <support/support.h>
+#include <support/subprocess.h>
+#include <support/xunistd.h>
+
+const char libs_dir[] = "/tmp/tst-no-default-paths-dlopen";
+const char elf_dir[] = "/elf";
+const char marker_lib[] = "/libmarkermod1.so";
+const char libc[] = "/libc.so.6";
+const char libdl[] = "/libdl.so.2";
+const char helper_program[] = "/tst-no-default-paths-helper";
+
+// Link system lib into libs_dir so the child process can access it
+static void
+link_system_lib_to_dir (const char *system_lib)
+{
+  char lib_src[4096];
+  strcpy(lib_src, support_slibdir_prefix);
+  strcat(lib_src, system_lib);
+
+  char lib_dest[4096];
+  strcpy(lib_dest, libs_dir);
+  strcat(lib_dest, system_lib);
+
+  unlink(lib_dest);
+  xsymlink(lib_src, lib_dest);
+}
+
+// Link libmarker into the system libs directory, where the child should *not*
+// be able to find it.
+static void
+link_libmarker (void)
+{
+  char marker_src[4096];
+  strcpy(marker_src, support_objdir_root);
+  strcat(marker_src, elf_dir);
+  strcat(marker_src, marker_lib);
+
+  char marker_dest[4096];
+  strcpy(marker_dest, support_install_prefix);
+  strcat(marker_dest, support_slibdir_prefix);
+  strcat(marker_dest, marker_lib);
+
+  unlink(marker_dest);
+  xsymlink(marker_src, marker_dest);
+}
+
+static int
+do_test (void)
+{
+  xmkdirp(libs_dir, 0755);
+
+  link_system_lib_to_dir(libc);
+  link_system_lib_to_dir(libdl);
+  link_libmarker();
+
+  char helper_path[4096];
+  strcpy(helper_path, support_objdir_root);
+  strcat(helper_path, elf_dir);
+  strcat(helper_path, helper_program);
+
+  char *const argv[] = {
+    strdup(support_container_elf_ldso_path),
+    strdup("--no-default-paths"),
+    strdup("--library-path"),
+    strdup(libs_dir),
+    helper_path,
+    strdup("dlopen"),
+    NULL
+  };
+
+  int ret = support_subprogram_wait(argv[0], argv);
+
+  return WEXITSTATUS(ret);
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-no-default-paths-helper.c b/elf/tst-no-default-paths-helper.c
new file mode 100644
index 0000000000..f1d390fdff
--- /dev/null
+++ b/elf/tst-no-default-paths-helper.c
@@ -0,0 +1,71 @@
+/* Helper for --no-default-paths tests
+   Copyright (C) 2021 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; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <stdio.h>
+
+const char marker_lib[] = "libmarkermod1.so";
+const char usage[] = "Call this as \"helper dlopen\" or \"helper dlinfo\"\n";
+
+int main(int argc, char *argv[]) {
+  if (argc < 2) {
+    FAIL_EXIT1(usage);
+  }
+
+  int mode;
+  if (strcmp(argv[1], "dlopen") == 0) {
+    mode = 0;
+  } else if (strcmp(argv[1], "dlinfo") == 0) {
+    mode = 1;
+  } else {
+    FAIL_EXIT1(usage);
+  }
+
+  void *handle = dlopen("libmarkermod1.so", RTLD_LAZY);
+  if (handle == NULL) {
+    if (mode) {
+      FAIL_EXIT1("Failed to load libmarkermod1.so\n");
+    }
+    return 0;
+  } else if (handle && mode == 0) {
+    FAIL_EXIT1("Loaded libmarkermod1.so successfully!\n");
+  }
+
+  Dl_serinfo *buffer = xmalloc(sizeof(*buffer));
+  if (dlinfo(handle, RTLD_DI_SERINFOSIZE, buffer)) {
+    FAIL_EXIT1("dlinfo failed: %s\n", dlerror());
+  }
+
+  buffer = xrealloc(buffer, buffer->dls_size);
+  if (dlinfo(handle, RTLD_DI_SERINFO, buffer)) {
+    FAIL_EXIT1("dlinfo failed: %s\n", dlerror());
+  }
+
+  // Checking if two paths are equal is tricky, so just check that there's
+  // exactly one and that it starts with /tmp, which should be sufficient to
+  // prove that the system paths have been excluded.
+  TEST_VERIFY_EXIT(buffer->dls_cnt == 1);
+  const char *name = buffer->dls_serpath[0].dls_name;
+  TEST_VERIFY_EXIT(strstr(name, "/tmp") == name);
+
+  return 0;
+}
diff --git a/support/Makefile b/support/Makefile
index da6dc58d37..d8399d3399 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -201,6 +201,7 @@ CFLAGS-support_paths.c = \
 		-DSRCDIR_PATH=\"`cd .. ; pwd`\" \
 		-DOBJDIR_PATH=\"`cd $(objpfx)/..; pwd`\" \
 		-DOBJDIR_ELF_LDSO_PATH=\"`cd $(objpfx)/..; pwd`/elf/$(rtld-installed-name)\" \
+		-DCONTAINER_ELF_LDSO_PATH=\"$(rtlddir)/$(rtld-installed-name)\" \
 		-DINSTDIR_PATH=\"$(prefix)\" \
 		-DLIBDIR_PATH=\"$(libdir)\" \
 		-DBINDIR_PATH=\"$(bindir)\" \
diff --git a/support/support.h b/support/support.h
index db264e3db7..c06130aebc 100644
--- a/support/support.h
+++ b/support/support.h
@@ -107,6 +107,10 @@ extern const char support_objdir_root[];
    e.g. OBJDIR_PATH/elf/ld-linux-x86-64.so.2  */
 extern const char support_objdir_elf_ldso[];
 
+/* Corresponds to the path to the runtime linker for containerised tests,
+   e.g. /lib64/ld-linux-x86-64.so.2  */
+extern const char support_container_elf_ldso_path[];
+
 /* Corresponds to the --prefix= passed to configure.  */
 extern const char support_install_prefix[];
 /* Corresponds to the install's lib/ or lib64/ directory.  */
diff --git a/support/support_paths.c b/support/support_paths.c
index d18e71e38b..4a14d4e78e 100644
--- a/support/support_paths.c
+++ b/support/support_paths.c
@@ -44,6 +44,12 @@ const char support_objdir_elf_ldso[] = OBJDIR_ELF_LDSO_PATH;
 # error please -DOBJDIR_ELF_LDSO_PATH=something in the Makefile
 #endif
 
+#ifdef CONTAINER_ELF_LDSO_PATH
+const char support_container_elf_ldso_path[] = CONTAINER_ELF_LDSO_PATH;
+#else
+# error please -DCONTAINER_ELF_LDSO_PATH=something in the Makefile
+#endif
+
 #ifdef INSTDIR_PATH
 /* Corresponds to the --prefix= passed to configure.  */
 const char support_install_prefix[] = INSTDIR_PATH;
-- 
2.31.1.818.g46aad6cb9e-goog

