From: "H.J. Lu" <hjl.tools@gmail.com>
To: libc-alpha@sourceware.org
Cc: Florian Weimer <fweimer@redhat.com>
Subject: [PATCH v6] elf: Don't execute shared object directly [BZ #28453]
Date: Sat, 11 Dec 2021 05:47:18 -0800 [thread overview]
Message-ID: <20211211134718.2048060-1-hjl.tools@gmail.com> (raw)
Changes in the v6 patch:
1. Remove ELF_MACHINE_DEBUG_SETUP.
2. Add <dl-debug.h> to setup and check debugging entry in PT_DYNAMIC
segment to support DT_DEBUG, DT_MIPS_RLD_MAP_REL and DT_MIPS_RLD_MAP.
Changes in the v5 patch:
1. Don't check DT_NEEDED since not all shared libraries have DT_NEEDED.
2. Update tst-rtld-run-dso test to use tst-ro-dynamic-mod.so which
doesn't have DT_NEEDED.
3. Check DT_DEBUG entry in PT_DYNAMIC segment to detect shared libraries.
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. Execute an ELF binary directly only if
1. It has a PT_INTERP segment, which covers dynamically linked
executables and libc.so. Or
2. It doesn't have a PT_DYNAMIC segment, which covers static non-PIE
executables. Or
3. It has debugging entry in PT_DYNAMIC segment, which covers static PIE
executables.
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)
$
Also simplify debugging entry processing in PT_DYNAMIC segment:
1. Remove ELF_MACHINE_DEBUG_SETUP.
2. Add <dl-debug.h> to setup and check debugging entry in PT_DYNAMIC
segment to support DT_DEBUG, DT_MIPS_RLD_MAP_REL and DT_MIPS_RLD_MAP.
This fixes [BZ #28453].
---
elf/Makefile | 11 ++++++++
elf/dl-reloc-static-pie.c | 11 ++------
elf/rtld.c | 47 +++++++++++++++++++---------------
elf/tst-rtld-run-dso.sh | 33 ++++++++++++++++++++++++
sysdeps/generic/dl-debug.h | 42 ++++++++++++++++++++++++++++++
sysdeps/mips/dl-debug.h | 52 ++++++++++++++++++++++++++++++++++++++
sysdeps/mips/dl-machine.h | 15 -----------
7 files changed, 167 insertions(+), 44 deletions(-)
create mode 100755 elf/tst-rtld-run-dso.sh
create mode 100644 sysdeps/generic/dl-debug.h
create mode 100644 sysdeps/mips/dl-debug.h
diff --git a/elf/Makefile b/elf/Makefile
index fe42caeb0e..d3f393eb96 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))
@@ -1892,6 +1896,13 @@ $(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)tst-ro-dynamic-mod.so
+ $(SHELL) tst-rtld-run-dso.sh $(objpfx)ld.so \
+ $(objpfx)tst-ro-dynamic-mod.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/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
index 5b85df8a2e..ad91721fe3 100644
--- a/elf/dl-reloc-static-pie.c
+++ b/elf/dl-reloc-static-pie.c
@@ -24,6 +24,7 @@
#include <ldsodefs.h>
#include <dl-machine.h>
+#include <dl-debug.h>
#define RESOLVE_MAP(map, scope, sym, version, flags) map
#include "dynamic-link.h"
@@ -68,14 +69,6 @@ _dl_relocate_static_pie (void)
/* Set up debugging before the debugger is notified for the first
time. */
-# ifdef ELF_MACHINE_DEBUG_SETUP
- /* Some machines (e.g. MIPS) don't use DT_DEBUG in this way. */
- ELF_MACHINE_DEBUG_SETUP (main_map, r);
-# else
- if (main_map->l_info[DT_DEBUG] != NULL)
- /* There is a DT_DEBUG entry in the dynamic section. Fill it in
- with the run-time address of the r_debug structure */
- main_map->l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
-# endif
+ elf_setup_debug_entry (main_map, r);
}
#endif
diff --git a/elf/rtld.c b/elf/rtld.c
index 4b09e84b0d..e73278c56c 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -51,6 +51,7 @@
#include <dl-tunables.h>
#include <get-dynamic-info.h>
#include <dl-execve.h>
+#include <dl-debug.h>
#include <assert.h>
@@ -1108,8 +1109,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 +1124,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 can be executed directly by
+ ld.so. Otherwise, it must be a static executable or a shared
+ library. */
+ bool has_pt_dynamic = false;
for (size_t i = 0; i < main_map->l_phnum; ++i)
if (main_map->l_phdr[i].p_type == PT_INTERP)
- return;
+ return true;
+ else if (main_map->l_phdr[i].p_type == PT_DYNAMIC)
+ has_pt_dynamic = true;
+ /* If there is no debugging entry in PT_DYNAMIC segment, it is a
+ shared library. */
+ if (has_pt_dynamic && elf_has_debug_entry_p (main_map))
+ return false;
+
+ /* This is a static executable. */
const char *pathname = _dl_argv[0];
if (argv0 != NULL)
_dl_argv[0] = argv0;
@@ -1144,6 +1151,7 @@ rtld_chain_load (struct link_map *main_map, char *argv0)
else
_dl_fatal_printf("%s: cannot execute %s: %d\n",
rtld_soname, pathname, errcode);
+ return true;
}
static void
@@ -1181,6 +1189,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 +1425,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;
@@ -1796,15 +1806,7 @@ dl_main (const ElfW(Phdr) *phdr,
size_t count_modids = _dl_count_modids ();
/* Set up debugging before the debugger is notified for the first time. */
-#ifdef ELF_MACHINE_DEBUG_SETUP
- /* Some machines (e.g. MIPS) don't use DT_DEBUG in this way. */
- ELF_MACHINE_DEBUG_SETUP (main_map, r);
-#else
- if (main_map->l_info[DT_DEBUG] != NULL)
- /* There is a DT_DEBUG entry in the dynamic section. Fill it in
- with the run-time address of the r_debug structure */
- main_map->l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
-#endif
+ elf_setup_debug_entry (main_map, r);
/* We start adding objects. */
r->r_state = RT_ADD;
@@ -2491,6 +2493,11 @@ 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\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"
diff --git a/sysdeps/generic/dl-debug.h b/sysdeps/generic/dl-debug.h
new file mode 100644
index 0000000000..87a3ccf795
--- /dev/null
+++ b/sysdeps/generic/dl-debug.h
@@ -0,0 +1,42 @@
+/* Debugging support. Generic version.
+ 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/>. */
+
+#ifndef _DL_DEBUG_H
+#define _DL_DEBUG_H
+
+/* There is a DT_DEBUG entry in the dynamic section. Fill it in with the
+ run-time address of the r_debug structure */
+
+static inline void
+__attribute ((always_inline))
+elf_setup_debug_entry (struct link_map *l, struct r_debug *r)
+{
+ if (l->l_info[DT_DEBUG] != NULL)
+ l->l_info[DT_DEBUG]->d_un.d_ptr = (ElfW(Addr)) r;
+}
+
+/* Return true if there is a DT_DEBUG entry in the dynamic section. */
+
+static inline bool
+__attribute ((always_inline))
+elf_has_debug_entry_p (struct link_map *l)
+{
+ return l->l_info[DT_DEBUG] != NULL;
+}
+
+#endif /* _DL_DEBUG_H */
diff --git a/sysdeps/mips/dl-debug.h b/sysdeps/mips/dl-debug.h
new file mode 100644
index 0000000000..789ad692a2
--- /dev/null
+++ b/sysdeps/mips/dl-debug.h
@@ -0,0 +1,52 @@
+/* Debugging support. MIPS version.
+ 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/>. */
+
+#ifndef _DL_DEBUG_H
+#define _DL_DEBUG_H
+
+/* If there is a DT_MIPS_RLD_MAP_REL or DT_MIPS_RLD_MAP entry in the
+ dynamic section, fill in the debug map pointer with the run-time
+ address of the r_debug structure. */
+
+static inline void
+__attribute ((always_inline))
+elf_setup_debug_entry (struct link_map *main, struct r_debug *r)
+{
+ if (l->l_info[DT_MIPS (RLD_MAP_REL)] != NULL)
+ {
+ char *ptr = (char *)l->l_info[DT_MIPS (RLD_MAP_REL)];
+ ptr += l->l_info[DT_MIPS (RLD_MAP_REL)]->d_un.d_val;
+ *(ElfW(Addr) *)ptr = (ElfW(Addr)) r;
+ }
+ else if (l->l_info[DT_MIPS (RLD_MAP)] != NULL)
+ *(ElfW(Addr) *)(l->l_info[DT_MIPS (RLD_MAP)]->d_un.d_ptr)
+ = (ElfW(Addr)) r;
+}
+
+/* Return true if there is a DT_MIPS_RLD_MAP_REL or DT_MIPS_RLD_MAP entry
+ in the dynamic section. */
+
+static inline void
+__attribute ((always_inline))
+elf_has_debug_entry_p (struct link_map *main)
+{
+ return (l->l_info[DT_MIPS (RLD_MAP_REL)] != NULL
+ || l->l_info[DT_MIPS (RLD_MAP)] != NULL);
+}
+
+#endif /* _DL_DEBUG_H */
diff --git a/sysdeps/mips/dl-machine.h b/sysdeps/mips/dl-machine.h
index d7b8341b74..ea8c881807 100644
--- a/sysdeps/mips/dl-machine.h
+++ b/sysdeps/mips/dl-machine.h
@@ -65,21 +65,6 @@
in l_info array. */
#define DT_MIPS(x) (DT_MIPS_##x - DT_LOPROC + DT_NUM)
-/* If there is a DT_MIPS_RLD_MAP_REL or DT_MIPS_RLD_MAP entry in the dynamic
- section, fill in the debug map pointer with the run-time address of the
- r_debug structure. */
-#define ELF_MACHINE_DEBUG_SETUP(l,r) \
-do { if ((l)->l_info[DT_MIPS (RLD_MAP_REL)]) \
- { \
- char *ptr = (char *)(l)->l_info[DT_MIPS (RLD_MAP_REL)]; \
- ptr += (l)->l_info[DT_MIPS (RLD_MAP_REL)]->d_un.d_val; \
- *(ElfW(Addr) *)ptr = (ElfW(Addr)) (r); \
- } \
- else if ((l)->l_info[DT_MIPS (RLD_MAP)]) \
- *(ElfW(Addr) *)((l)->l_info[DT_MIPS (RLD_MAP)]->d_un.d_ptr) = \
- (ElfW(Addr)) (r); \
- } while (0)
-
#if ((defined __mips_nan2008 && !defined HAVE_MIPS_NAN2008) \
|| (!defined __mips_nan2008 && defined HAVE_MIPS_NAN2008))
# error "Configuration inconsistency: __mips_nan2008 != HAVE_MIPS_NAN2008, overridden CFLAGS?"
--
2.33.1
reply other threads:[~2021-12-11 13:47 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20211211134718.2048060-1-hjl.tools@gmail.com \
--to=hjl.tools@gmail.com \
--cc=fweimer@redhat.com \
--cc=libc-alpha@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).