From c1cb2deeca1a85c6fc5bd41b90816d48a95bc434 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Sun, 5 Dec 2021 11:28:34 +0100 Subject: [PATCH] elf: execve statically linked programs instead of crashing [BZ #28648] Programs without dynamic dependencies and without a program interpreter are now run via execve. Previously, the dynamic linker either crashed while attempting to read a non-existing dynamic segment (looking for DT_AUDIT/DT_DEPAUDIT data), or the self-relocated in the static PIE executable crashed because the outer dynamic linker had already applied RELRO protection. is needed because execve is not available in the dynamic loader on Hurd. Reviewed-by: H.J. Lu --- NEWS | 4 ++ elf/Makefile | 3 ++ elf/rtld.c | 50 +++++++++++++++++++---- elf/tst-rtld-run-static.c | 62 +++++++++++++++++++++++++++++ sysdeps/generic/dl-execve.h | 25 ++++++++++++ sysdeps/unix/sysv/linux/dl-execve.h | 25 ++++++++++++ 6 files changed, 161 insertions(+), 8 deletions(-) create mode 100644 elf/tst-rtld-run-static.c create mode 100644 sysdeps/generic/dl-execve.h create mode 100644 sysdeps/unix/sysv/linux/dl-execve.h diff --git a/NEWS b/NEWS index f10971b180..1398cf2e87 100644 --- a/NEWS +++ b/NEWS @@ -184,6 +184,10 @@ Major new features: than or equal to a given integer. This function is a GNU extension, although Solaris also provides a similar function. +* When invoked explicitly, the dynamic linker now uses the kernel to + execute programs that do not have any dynamic dependency (that is, + they are statically linked). This feature is Linux-specific. + Deprecated and removed features, and other changes affecting compatibility: * The function pthread_mutex_consistent_np has been deprecated; programs diff --git a/elf/Makefile b/elf/Makefile index 4723c159cb..ef36008673 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -231,6 +231,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ tst-tls20 tst-tls21 tst-dlmopen-dlerror tst-dlmopen-gethostbyname \ tst-dl-is_dso tst-ro-dynamic \ tst-audit18 \ + tst-rtld-run-static \ # reldep9 tests-internal += loadtest unload unload2 circleload1 \ neededtest neededtest2 neededtest3 neededtest4 \ @@ -1977,3 +1978,5 @@ $(objpfx)tst-ro-dynamic-mod.so: $(objpfx)tst-ro-dynamic-mod.os \ $(LINK.o) -nostdlib -nostartfiles -shared -o $@ \ -Wl,--script=tst-ro-dynamic-mod.map \ $(objpfx)tst-ro-dynamic-mod.os + +$(objpfx)tst-rtld-run-static.out: $(objpfx)/ldconfig diff --git a/elf/rtld.c b/elf/rtld.c index 847141e21d..6ce1e07dc0 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -1106,6 +1107,45 @@ 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 +rtld_chain_load (struct link_map *main_map, char *argv0) +{ + /* The dynamic loader run against itself. */ + const char *rtld_soname + = ((const char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB]) + + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_val); + if (main_map->l_info[DT_SONAME] != NULL + && strcmp (rtld_soname, + ((const char *) D_PTR (main_map, l_info[DT_STRTAB]) + + 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. */ + for (size_t i = 0; i < main_map->l_phnum; ++i) + if (main_map->l_phdr[i].p_type == PT_INTERP) + return; + + const char *pathname = _dl_argv[0]; + if (argv0 != NULL) + _dl_argv[0] = argv0; + int errcode = __rtld_execve (pathname, _dl_argv, _environ); + const char *errname = strerrorname_np (errcode); + if (errname != NULL) + _dl_fatal_printf("%s: cannot execute %s: %s\n", + rtld_soname, pathname, errname); + else + _dl_fatal_printf("%s: cannot execute %s: %d\n", + rtld_soname, pathname, errno); +} + static void dl_main (const ElfW(Phdr) *phdr, ElfW(Word) phnum, @@ -1374,14 +1414,8 @@ dl_main (const ElfW(Phdr) *phdr, /* Now the map for the main executable is available. */ main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; - if (__glibc_likely (state.mode == rtld_mode_normal) - && GL(dl_rtld_map).l_info[DT_SONAME] != NULL - && main_map->l_info[DT_SONAME] != NULL - && strcmp ((const char *) D_PTR (&GL(dl_rtld_map), l_info[DT_STRTAB]) - + GL(dl_rtld_map).l_info[DT_SONAME]->d_un.d_val, - (const char *) D_PTR (main_map, l_info[DT_STRTAB]) - + main_map->l_info[DT_SONAME]->d_un.d_val) == 0) - _dl_fatal_printf ("loader cannot load itself\n"); + if (__glibc_likely (state.mode == rtld_mode_normal)) + rtld_chain_load (main_map, argv0); phdr = main_map->l_phdr; phnum = main_map->l_phnum; diff --git a/elf/tst-rtld-run-static.c b/elf/tst-rtld-run-static.c new file mode 100644 index 0000000000..7281093504 --- /dev/null +++ b/elf/tst-rtld-run-static.c @@ -0,0 +1,62 @@ +/* Test running statically linked programs using ld.so. + 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 + . */ + +#include +#include +#include +#include +#include + +static int +do_test (void) +{ + char *ldconfig_path = xasprintf ("%s/elf/ldconfig", support_objdir_root); + + { + char *argv[] = { (char *) "ld.so", ldconfig_path, (char *) "--help", NULL }; + struct support_capture_subprocess cap + = support_capture_subprogram (support_objdir_elf_ldso, argv); + support_capture_subprocess_check (&cap, "no --argv0", 0, sc_allow_stdout); + puts ("info: output without --argv0:"); + puts (cap.out.buffer); + TEST_VERIFY (strstr (cap.out.buffer, "Usage: ldconfig [OPTION...]\n") + == cap.out.buffer); + support_capture_subprocess_free (&cap); + } + + { + char *argv[] = + { + (char *) "ld.so", (char *) "--argv0", (char *) "ldconfig-argv0", + ldconfig_path, (char *) "--help", NULL + }; + struct support_capture_subprocess cap + = support_capture_subprogram (support_objdir_elf_ldso, argv); + support_capture_subprocess_check (&cap, "with --argv0", 0, sc_allow_stdout); + puts ("info: output with --argv0:"); + puts (cap.out.buffer); + TEST_VERIFY (strstr (cap.out.buffer, "Usage: ldconfig-argv0 [OPTION...]\n") + == cap.out.buffer); + support_capture_subprocess_free (&cap); + } + + free (ldconfig_path); + return 0; +} + +#include diff --git a/sysdeps/generic/dl-execve.h b/sysdeps/generic/dl-execve.h new file mode 100644 index 0000000000..5fd097df69 --- /dev/null +++ b/sysdeps/generic/dl-execve.h @@ -0,0 +1,25 @@ +/* execve for the dynamic linker. Generic stub 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 + . */ + +#include + +static int +__rtld_execve (const char *path, char *const *argv, char *const *envp) +{ + return ENOSYS; +} diff --git a/sysdeps/unix/sysv/linux/dl-execve.h b/sysdeps/unix/sysv/linux/dl-execve.h new file mode 100644 index 0000000000..ead3e1c28d --- /dev/null +++ b/sysdeps/unix/sysv/linux/dl-execve.h @@ -0,0 +1,25 @@ +/* execve for the dynamic linker. Linux 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 + . */ + +#include + +static inline int +__rtld_execve (const char *path, char *const *argv, char *const *envp) +{ + return -INTERNAL_SYSCALL_CALL (execve, path, argv, envp); +}