From patchwork Sat Dec 11 13:47:18 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: 48829 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 DF0333857C76 for ; Sat, 11 Dec 2021 13:47:43 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DF0333857C76 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1639230463; bh=a6DNhlyrj0D3iLnQjA1sgyxgV8Tb1ngp93rcKXLEpcw=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=KQQItRvF9v2adPMzMMLy54hgHy2yiaAkxoDxYTNyiHj3nSwE1yLDxwiIfIQV8sXFm Cekg9fapYTt83RjEEaoxnen1t4LpECI420w0X8sRP621wbBWktWj1gQcv4qGGB5H7I sYZuqEGvs1kv+mDKdcuCGWFncKFoOQiYU9L070VQ= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pj1-x1036.google.com (mail-pj1-x1036.google.com [IPv6:2607:f8b0:4864:20::1036]) by sourceware.org (Postfix) with ESMTPS id 64F2F3858405 for ; Sat, 11 Dec 2021 13:47:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 64F2F3858405 Received: by mail-pj1-x1036.google.com with SMTP id fv9-20020a17090b0e8900b001a6a5ab1392so9772462pjb.1 for ; Sat, 11 Dec 2021 05:47:21 -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=a6DNhlyrj0D3iLnQjA1sgyxgV8Tb1ngp93rcKXLEpcw=; b=ecvv2XXBRMc5T/cPBNUCMoDUvF5iANT4PwJ+vcBEBioohGYAV6RyI5cT09y2QApS8e D5cHe1wzwaUZ3skuUqRfU7LHdLU9V+36QwmWK6p8opIR+2Ob/Cc1Lm3vm4uWlq3H5duV obfTh3qgVcKCaZZveicrO691iYJDmL2stefhwTq+AEU4mtCc8jmdKlbVVjLS3NwzTqVt lxml2AB+aB6ZnpSoVKq8sFYk7OuMwWhnXcDujnIYFmo41i7pffCX6HHEf3jnBOog3H3d gf4DhM14j3nV9CiKXfcyLIfiwXZwNsiwnDGSlB5VG4KEN6KaTFEWfZGPPjs4/+bLv9zw p4ug== X-Gm-Message-State: AOAM532peCsGsdfDdJFwW0dwD7VGxe1N47qJNB/bCGgHq2foefm4aRmT MKdKumI65v0RxUJ9dZqCN5GG84u8slE= X-Google-Smtp-Source: ABdhPJzKSXpKVAbIhg59kPIXCA6jaQIhv01bc5d8P+DgE1MVI4y1waRYZEXB5vTBiLDt0H234TdEmg== X-Received: by 2002:a17:90a:e7ca:: with SMTP id kb10mr31054519pjb.8.1639230440126; Sat, 11 Dec 2021 05:47:20 -0800 (PST) Received: from gnu-cfl-2.localdomain ([172.58.35.133]) by smtp.gmail.com with ESMTPSA id o15sm5349432pgk.7.2021.12.11.05.47.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 11 Dec 2021 05:47:19 -0800 (PST) Received: from gnu-cfl-2.. (localhost [IPv6:::1]) by gnu-cfl-2.localdomain (Postfix) with ESMTP id A8A7F420118; Sat, 11 Dec 2021 05:47:18 -0800 (PST) To: libc-alpha@sourceware.org Subject: [PATCH v6] elf: Don't execute shared object directly [BZ #28453] Date: Sat, 11 Dec 2021 05:47:18 -0800 Message-Id: <20211211134718.2048060-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 v6 patch: 1. Remove ELF_MACHINE_DEBUG_SETUP. 2. Add 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: . $ ./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 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 #include +#include #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 #include #include +#include #include @@ -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 +# . + +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 + . */ + +#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 + . */ + +#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?"