From patchwork Fri Dec 10 18:37:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "H.J. Lu" X-Patchwork-Id: 48802 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 98B4B3857C6C for ; Fri, 10 Dec 2021 18:37:35 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 98B4B3857C6C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1639161455; bh=Kf0ms49gE+cCmrLnAPujrJEKJYnlWRxGbGxNQnSgPW0=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=FNNbGG4TeL9snytOsdwNIYXzEQ925CNKFClpIIxglxro8UW7nt71eQUjKJQdFrhn2 dphBx/EKKCMGWMOAXw4tqR28O/NZBaaa85l7nCiwa0QKtxHgAOxca9Xtag2y+XjwIX RK3sXjpqI3h2ZntVttDB6nEQmp5wZv5SqOue/SPA= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pf1-x431.google.com (mail-pf1-x431.google.com [IPv6:2607:f8b0:4864:20::431]) by sourceware.org (Postfix) with ESMTPS id E4CD43858031 for ; Fri, 10 Dec 2021 18:37:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org E4CD43858031 Received: by mail-pf1-x431.google.com with SMTP id k64so9176337pfd.11 for ; Fri, 10 Dec 2021 10:37:12 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=Kf0ms49gE+cCmrLnAPujrJEKJYnlWRxGbGxNQnSgPW0=; b=60ydOuGq94G1tyxv6b0NbVRY3EB5Z5G20hNOAmMfAb0mOSd8ZyJK1KtnMJgXpmJuVp yEKDwED8Z97LKk5qyi18gH0t9l45EJD3IeyAyNd8C1DsyBQ8QMBz8b8wbUZS5Uk8LzwE ioV0EyyL6k/j3GUysjZ9CIcMRrbjrGRcRMC2cGwKlyDUq5flWvMtt4gc6tJbVN4e0MJa LBqaTzxqoEXYhaZ+lrA4WFXOchAU8CSP5phal/ANnnUh06ocAueHn0om1WG3SP8oS9Dj vnjLzvcCquvtGKCNmlSfPT0Qv4AetakP4p+9bFttXoCbrLtJaiVrHooccXAFHwIZArQP BW9A== X-Gm-Message-State: AOAM531mnaO24FHfFyEgUPmWP/8rJg2jJWYsS67lvPBXI208Te3+gpB5 ASa6U+FHecccOjnGlH5TGPB/exfe/2c= X-Google-Smtp-Source: ABdhPJxFar5xdtvOuhTlbkU5V3+H9K8V80G3DqpowIBtr4MfXqBBbdzKmMdZyyNNrpgmk0TisRS4WQ== X-Received: by 2002:a05:6a00:2af:b0:49f:c4b0:879b with SMTP id q15-20020a056a0002af00b0049fc4b0879bmr19484051pfs.55.1639161431733; Fri, 10 Dec 2021 10:37:11 -0800 (PST) Received: from gnu-cfl-2.localdomain ([172.58.35.133]) by smtp.gmail.com with ESMTPSA id f3sm3864424pfg.167.2021.12.10.10.37.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Dec 2021 10:37:11 -0800 (PST) Received: from gnu-tgl-2.localdomain (gnu-tgl-2 [192.168.1.34]) by gnu-cfl-2.localdomain (Postfix) with ESMTPS id AB997421579; Fri, 10 Dec 2021 10:37:10 -0800 (PST) Received: from gnu-tgl-2.. (localhost [IPv6:::1]) by gnu-tgl-2.localdomain (Postfix) with ESMTP id A19CD300350; Fri, 10 Dec 2021 10:37:09 -0800 (PST) To: libc-alpha@sourceware.org Subject: [PATCH v5] elf: Don't execute shared object directly [BZ #28453] Date: Fri, 10 Dec 2021 10:37:09 -0800 Message-Id: <20211210183709.3906390-1-hjl.tools@gmail.com> X-Mailer: git-send-email 2.33.1 MIME-Version: 1.0 X-Spam-Status: No, score=-3028.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_BARRACUDACENTRAL, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLACK 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: "H.J. Lu via Libc-alpha" From: "H.J. Lu" Reply-To: "H.J. Lu" Cc: Florian Weimer Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" 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 DT_DEBUG 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: . $ ./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 | 11 +++++++++++ elf/rtld.c | 37 ++++++++++++++++++++++++++----------- elf/tst-rtld-run-dso.sh | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 11 deletions(-) create mode 100755 elf/tst-rtld-run-dso.sh diff --git a/elf/Makefile b/elf/Makefile index ef36008673..2b85bfa03b 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,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/rtld.c b/elf/rtld.c index 6ce1e07dc0..6db3c62c80 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,23 @@ 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 DT_DEBUG entry in PT_DYNAMIC segment, it is a shared + library. */ + if (has_pt_dynamic + && __glibc_unlikely (main_map->l_info[DT_DEBUG] == NULL)) + 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, errno); + 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; @@ -2491,6 +2501,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 +# . + +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"