From: Florian Weimer Date: Sun, 5 Dec 2021 10:28:34 +0000 (+0100) Subject: elf: execve statically linked programs instead of crashing [BZ #28648] X-Git-Tag: glibc-2.35~290 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c1cb2deeca1a85c6fc5bd41b90816d48a95bc434;p=thirdparty%2Fglibc.git 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 --- diff --git a/NEWS b/NEWS index f10971b180d..1398cf2e87c 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 4723c159cb0..ef360086731 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 847141e21db..6ce1e07dc0b 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 00000000000..7281093504b --- /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 00000000000..5fd097df69e --- /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 00000000000..ead3e1c28da --- /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); +}