From patchwork Sat Dec 11 20:07:05 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: 48835 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 1449C385780E for ; Sat, 11 Dec 2021 20:07:32 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1449C385780E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1639253252; bh=99OozEiSDazC4mhlxUPLxjKaT9jks7/ooJ8PHv0URsM=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=ax/vdRQ68m5WZ141Ha3393eiYp7PQRJ0j3ob/2FFNirBoLBWJPwo9AZrjrNbC05Yw tZ0IOKPrqvpul22S8lNb3oS7DTTVxjMIyCrvfsMKT6GReK75MSNnAW88yclfdbpqEp cU2yrOS4DoBrR1yWY6Xruwzthq0a01rbHWIiRQIE= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) by sourceware.org (Postfix) with ESMTPS id 466CF3858D39 for ; Sat, 11 Dec 2021 20:07:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 466CF3858D39 Received: by mail-pl1-x631.google.com with SMTP id n8so8502564plf.4 for ; Sat, 11 Dec 2021 12:07:08 -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=99OozEiSDazC4mhlxUPLxjKaT9jks7/ooJ8PHv0URsM=; b=eHBxUkQ1o0KuATHIL9N5+AcTPEQOwn6MbNVOi3RLKkgzyPAtEYJ89pnw+OSJgQDtn4 9WI5Cv8yVjDv+OP3RPFGCfje2PQePilfO7itFeYYhJfA/10/SBk74J71ppFt2EXrDnyY oe+rXv2FlfD2xsQ1ex/ZNZQSHwj+uRsijwfbLccFOBqfBp+kcrVhvWWFBv2LFeyiov79 +GwxlEb4ZPxKzGGaIskQ4cBSnOFvTbLlywv2lh0nzGxMSFC7kn6ZAeA470Rx4XapPhfT xaeeL+q1xucF16mNUu7E3/GZDs4W5AVwsb+5y/Mr7sxpcZcUl5EyEtDW5awm7quJ2No7 Qi2g== X-Gm-Message-State: AOAM530iRbUY51gT8E9KO85DXmW1v+aPg1NoJ5zuOmUkbxQQEldj1yuJ 9V6omjPPUaRoxaZWZEUXJpagM93HYTE= X-Google-Smtp-Source: ABdhPJwHRMkL3ImO2hOynJ+NYrlfqJbN8VyCk47lwJrWj7tZmfeJxLjKxFtCd3wl31SlhuVNxrk8mg== X-Received: by 2002:a17:90a:ec15:: with SMTP id l21mr32639088pjy.48.1639253227091; Sat, 11 Dec 2021 12:07:07 -0800 (PST) Received: from gnu-cfl-2.localdomain ([172.58.35.133]) by smtp.gmail.com with ESMTPSA id l6sm7852431pfc.51.2021.12.11.12.07.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 11 Dec 2021 12:07:06 -0800 (PST) Received: from gnu-cfl-2.. (localhost [IPv6:::1]) by gnu-cfl-2.localdomain (Postfix) with ESMTP id C1BDD420972; Sat, 11 Dec 2021 12:07:05 -0800 (PST) To: libc-alpha@sourceware.org Subject: [PATCH v7] elf: Don't execute shared object directly [BZ #28453] Date: Sat, 11 Dec 2021 12:07:05 -0800 Message-Id: <20211211200705.3973-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 v7 patch: 1. Check !elf_has_debug_entry_p instead of elf_has_debug_entry_p. 2. Fix typos in sysdeps/mips/dl-debug.h. 3. Run the tst-rtld-run-dso test only if $(run-built-tests) == yes. 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' directly $ 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]. Tested on x86-64, x32 and i686 as well as with build-many-glibcs.py. --- elf/Makefile | 10 +++++++- elf/dl-reloc-static-pie.c | 11 ++------ elf/rtld.c | 49 ++++++++++++++++++++--------------- 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(+), 45 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..1337ec2d0b 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -47,7 +47,8 @@ tunables-type = $(addprefix TUNABLES_FRONTEND_,$(have-tunables)) CPPFLAGS-dl-tunables.c += -DTUNABLES_FRONTEND=$(tunables-type) ifeq (yesyes,$(build-shared)$(run-built-tests)) -tests-special += $(objpfx)list-tunables.out +tests-special += $(objpfx)list-tunables.out \ + $(objpfx)tst-rtld-run-dso.out endif # Make sure that the compiler does not insert any library calls in tunables @@ -1892,6 +1893,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..e1b3ccce76 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -62,6 +62,9 @@ #define RESOLVE_MAP(map, scope, sym, version, flags) map #include "dynamic-link.h" +/* Must include after for DT_MIPS definition. */ +#include + /* Only enables rtld profiling for architectures which provides non generic hp-timing support. The generic support requires either syscall (clock_gettime), which will incur in extra overhead on loading time. @@ -1108,8 +1111,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 +1126,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 +1153,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 +1191,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 +1427,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 +1808,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 +2495,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..85b2920a4b --- /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 *l, 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 bool +__attribute ((always_inline)) +elf_has_debug_entry_p (struct link_map *l) +{ + 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?"