public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v4] elf: Only allow execute libc.so.6 directly [BZ #28453]
@ 2021-12-10 14:52 H.J. Lu
  2021-12-10 14:58 ` Florian Weimer
  0 siblings, 1 reply; 5+ messages in thread
From: H.J. Lu @ 2021-12-10 14:52 UTC (permalink / raw)
  To: libc-alpha; +Cc: Florian Weimer

Changes in the v4 patch:

1. Only allow executing executables and libc.s.6 directly.

Changes in the v3 patch:

1. Delay zero entry point value check.
2. Build testobj1.so with -Wl,--entry=0

Changes in the v2 patch:

1. Use rtld_progname in the error message.

A shared library can have an invalid non-zero entry point value generated
by the old linkers, which leads to ld.so crash when it executes such
shared library directly.  Since we can't rely on entry point values to
decide if it is a valid entry point, we should only allow executing
executables and libc.s.6 directly.  Now we get

$ ./elf/ld.so ./libc.so.6
GNU C Library (GNU libc) development release version 2.34.9000.
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 11.2.1 20211203 (Red Hat 11.2.1-7).
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
<https://www.gnu.org/software/libc/bugs.html>.
$ ./elf/ld.so /lib64/libstdc++.so.6.0.29
./elf/ld.so: cannot execute '/lib64/libstdc++.so.6.0.29' without entry point
$

instead of

$ /lib64/ld-linux-x86-64.so.2 /lib64/libstdc++.so.6.0.29
Segmentation fault (core dumped)
$

This fixes [BZ #28453].
---
 elf/Makefile            | 10 ++++++++++
 elf/rtld.c              | 37 ++++++++++++++++++++++++++-----------
 elf/tst-rtld-run-dso.sh | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+), 11 deletions(-)
 create mode 100755 elf/tst-rtld-run-dso.sh

diff --git a/elf/Makefile b/elf/Makefile
index ef36008673..1832dfa537 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -50,6 +50,10 @@ ifeq (yesyes,$(build-shared)$(run-built-tests))
 tests-special += $(objpfx)list-tunables.out
 endif
 
+ifeq (yes,$(build-shared))
+tests-special += $(objpfx)tst-rtld-run-dso.out
+endif
+
 # Make sure that the compiler does not insert any library calls in tunables
 # code paths.
 ifeq (yes,$(have-loop-to-function))
@@ -1877,6 +1881,12 @@ $(objpfx)list-tunables.out: tst-rtld-list-tunables.sh $(objpfx)ld.so
 	    $(objpfx)/tst-rtld-list-tunables.out > $@; \
 	$(evaluate-test)
 
+$(objpfx)tst-rtld-run-dso.out: tst-rtld-run-dso.sh $(objpfx)ld.so \
+			    $(objpfx)testobj1.so
+	$(SHELL) tst-rtld-run-dso.sh $(objpfx)ld.so $(objpfx)testobj1.so \
+	    '$(test-wrapper-env)' '$(run_program_env)' > $@
+	$(evaluate-test)
+
 tst-dst-static-ENV = LD_LIBRARY_PATH='$$ORIGIN'
 
 $(objpfx)tst-rtld-help.out: $(objpfx)ld.so
diff --git a/elf/rtld.c b/elf/rtld.c
index 6ce1e07dc0..f6b80444fa 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1108,8 +1108,9 @@ load_audit_modules (struct link_map *main_map, struct audit_list *audit_list)
 }
 
 /* Check if the executable is not actualy dynamically linked, and
-   invoke it directly in that case.  */
-static void
+   invoke it directly in that case.  Return true if it can be executed
+   directly by ld.so.  */
+static bool
 rtld_chain_load (struct link_map *main_map, char *argv0)
 {
   /* The dynamic loader run against itself.  */
@@ -1122,17 +1123,22 @@ rtld_chain_load (struct link_map *main_map, char *argv0)
 		  + main_map->l_info[DT_SONAME]->d_un.d_val)) == 0)
     _dl_fatal_printf ("%s: loader cannot load itself\n", rtld_soname);
 
-  /* With DT_NEEDED dependencies, the executable is dynamically
-     linked.  */
-  if (__glibc_unlikely (main_map->l_info[DT_NEEDED] != NULL))
-    return;
-
-  /* If the executable has program interpreter, it is dynamically
-     linked.  */
+  /* If it has program interpreter, it is dynamically linked
+     executable.  */
   for (size_t i = 0; i < main_map->l_phnum; ++i)
     if (main_map->l_phdr[i].p_type == PT_INTERP)
-      return;
+      return true;
+
+  /* With DT_NEEDED dependencies, it is a shared library.  Only allow
+     execute libc.so directly.  */
+  if (__glibc_unlikely (main_map->l_info[DT_NEEDED] != NULL))
+    return (main_map->l_info[DT_SONAME] != NULL
+	    && strncmp ("libc.so.6",
+			((const char *) D_PTR (main_map, l_info[DT_STRTAB])
+			 + main_map->l_info[DT_SONAME]->d_un.d_val),
+			sizeof ("libc.so.6") - 1) == 0);
 
+  /* This is a static executable.  */
   const char *pathname = _dl_argv[0];
   if (argv0 != NULL)
     _dl_argv[0] = argv0;
@@ -1144,6 +1150,7 @@ rtld_chain_load (struct link_map *main_map, char *argv0)
   else
     _dl_fatal_printf("%s: cannot execute %s: %d\n",
 		     rtld_soname, pathname, errno);
+  return true;
 }
 
 static void
@@ -1181,6 +1188,8 @@ dl_main (const ElfW(Phdr) *phdr,
   _dl_starting_up = 1;
 #endif
 
+  bool can_execute = true;
+
   const char *ld_so_name = _dl_argv[0];
   if (*user_entry == (ElfW(Addr)) ENTRY_POINT)
     {
@@ -1415,7 +1424,7 @@ dl_main (const ElfW(Phdr) *phdr,
       main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
 
       if (__glibc_likely (state.mode == rtld_mode_normal))
-	rtld_chain_load (main_map, argv0);
+	can_execute = rtld_chain_load (main_map, argv0);
 
       phdr = main_map->l_phdr;
       phnum = main_map->l_phnum;
@@ -2491,6 +2500,12 @@ dl_main (const ElfW(Phdr) *phdr,
       rtld_timer_accum (&relocate_time, start);
     }
 
+  /* Stop if it can't be executed.  */
+  if (!can_execute)
+    _dl_fatal_printf("%s: cannot execute shared object '%s' directly "
+		     "without entry point\n",
+		     ld_so_name, rtld_progname);
+
   /* Relocation is complete.  Perform early libc initialization.  This
      is the initial libc, even if audit modules have been loaded with
      other libcs.  */
diff --git a/elf/tst-rtld-run-dso.sh b/elf/tst-rtld-run-dso.sh
new file mode 100755
index 0000000000..5192f64210
--- /dev/null
+++ b/elf/tst-rtld-run-dso.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Test for ld.so on a shared library with no associated entry point.
+# 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/>.
+
+set -e
+
+rtld=$1
+dso=$2
+test_wrapper_env=$3
+run_program_env=$4
+
+LC_ALL=C
+export LC_ALL
+
+${test_wrapper_env} \
+${run_program_env} \
+$rtld $dso 2>&1 \
+| grep "cannot execute"
-- 
2.33.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2021-12-10 18:38 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-10 14:52 [PATCH v4] elf: Only allow execute libc.so.6 directly [BZ #28453] H.J. Lu
2021-12-10 14:58 ` Florian Weimer
2021-12-10 15:26   ` H.J. Lu
2021-12-10 16:06     ` Florian Weimer
2021-12-10 18:37       ` H.J. Lu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).