From patchwork Sat Dec 4 19:05:13 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 48495 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 764F93858424 for ; Sat, 4 Dec 2021 19:05:45 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 764F93858424 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1638644745; bh=kQ++tU7FVXtZ3kPvRkR7eq9Ww7h17CTtKNudK27aCvM=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=YPJGJ4ihQb4N9o1aX8uNjw2Z8b6vINCy66nDZM1YYvRim1DNdVVV4VlEDuHJbZs6Z FxwTY/uBOmaQU4MvuiQDJ91jzHzshbLcdDRHN7KNT32Zfxw3sFY4y98wLfcJLoqjyq M1Sl6+epFqhF7Vu7k562Rj9ojR4ZxzTK3SWHkgXw= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by sourceware.org (Postfix) with ESMTPS id 8C4A53858D28 for ; Sat, 4 Dec 2021 19:05:22 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 8C4A53858D28 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-97-JvJdK0VwMJquRUcymQVxXA-1; Sat, 04 Dec 2021 14:05:17 -0500 X-MC-Unique: JvJdK0VwMJquRUcymQVxXA-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id DBBC12E61; Sat, 4 Dec 2021 19:05:16 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.39.193.123]) by smtp.corp.redhat.com (Postfix) with ESMTPS id C3D5660BE5; Sat, 4 Dec 2021 19:05:15 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH] elf: execve statically linked programs instead of crashing [BZ #28648] Date: Sat, 04 Dec 2021 20:05:13 +0100 Message-ID: <87sfv8tcly.fsf@oldenburg.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Florian Weimer via Libc-alpha From: Florian Weimer Reply-To: Florian Weimer Cc: Aurelien Jarno Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" 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. Tested on i686-linux-gnu and x86_64-linux-gnu. Built with build-many-glibcs.py. --- NEWS | 4 +++ elf/Makefile | 3 ++ elf/rtld.c | 54 +++++++++++++++++++++++++++----- elf/tst-rtld-run-static.c | 62 +++++++++++++++++++++++++++++++++++++ sysdeps/generic/dl-execve.h | 25 +++++++++++++++ sysdeps/unix/sysv/linux/dl-execve.h | 25 +++++++++++++++ 6 files changed, 165 insertions(+), 8 deletions(-) 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..4291fe8640 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -1106,6 +1107,49 @@ 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 = "ld.so"; + if (GL(dl_rtld_map).l_info[DT_SONAME] != NULL) + { + 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 (rtld_soname != NULL + && 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 +1418,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); +}