From patchwork Wed Sep 15 01:36:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 45004 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 554213857C71 for ; Wed, 15 Sep 2021 01:37:41 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 554213857C71 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1631669861; bh=qUysR1dVcQEWrbfovWD/8SKwJoEce3jkaPnx1yvHkD8=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=qWsmyLma4gs3Soiv/HZIY3cum82R3+5aCq2cLyJL1TKggT4mFFT0gE4bvvQQYd71c qHBkHRxP+8HSRXeA1aHtGrS8pXLUMpMEQzDwmq3NHhqxErjQHNJEMQDeSUWYDsO1L8 /ckaXzCVhfo6JScfRg6lhAEtZecNCIl+WqVxY12A= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from bat.birch.relay.mailchannels.net (bat.birch.relay.mailchannels.net [23.83.209.13]) by sourceware.org (Postfix) with ESMTPS id D5ED23858402 for ; Wed, 15 Sep 2021 01:37:18 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org D5ED23858402 X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id A02316E062E; Wed, 15 Sep 2021 01:37:17 +0000 (UTC) Received: from pdx1-sub0-mail-a8.g.dreamhost.com (unknown [127.0.0.6]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 2191C6E09E5; Wed, 15 Sep 2021 01:37:16 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a8.g.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384) by 100.100.96.79 (trex/6.4.3); Wed, 15 Sep 2021 01:37:17 +0000 X-MC-Relay: Junk X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Eyes-Desert: 3bcf53a26412b645_1631669837496_2841179843 X-MC-Loop-Signature: 1631669837496:3916304989 X-MC-Ingress-Time: 1631669837495 Received: from pdx1-sub0-mail-a8.g.dreamhost.com (localhost [127.0.0.1]) by pdx1-sub0-mail-a8.g.dreamhost.com (Postfix) with ESMTP id D74298A5C1; Tue, 14 Sep 2021 18:37:15 -0700 (PDT) Received: from rhbox.redhat.com (unknown [1.186.224.23]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a8.g.dreamhost.com (Postfix) with ESMTPSA id 5CF588A59C; Tue, 14 Sep 2021 18:37:12 -0700 (PDT) X-DH-BACKEND: pdx1-sub0-mail-a8 To: libc-alpha@sourceware.org Subject: [PATCH v2] ld.so: Handle read-only dynamic section gracefully [BZ #28340] Date: Wed, 15 Sep 2021 07:06:53 +0530 Message-Id: <20210915013653.1802776-1-siddhesh@sourceware.org> X-Mailer: git-send-email 2.31.1 MIME-Version: 1.0 X-Spam-Status: No, score=-3495.3 required=5.0 tests=BAYES_00, GIT_PATCH_0, JMQ_SPF_NEUTRAL, KAM_DMARC_NONE, KAM_DMARC_STATUS, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NEUTRAL, 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: Siddhesh Poyarekar via Libc-alpha From: Siddhesh Poyarekar Reply-To: Siddhesh Poyarekar Cc: fweimer@redhat.com Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" ld.so on x86_64 (and possibly for all other architectures that don't have a read-only dynamic section by default but I haven't checked on all of them), when invoked with a DSO that has a read-only .dynamic section, crashes when trying to update gotplt, symtab, etc. entries with the load address. This crash happens even during verification, i.e. when it is invoked with --verify or with LD_TRACE_LOADED_OBJECTS. This is seen with vdso64.so from the Linux kernel on x86_64 for example. Handle this case more gracefully by bailing out early when a read-only PT_DYNAMIC segment is encountered and l_addr is non-zero, indicating the need for fixing up entries in .dynamic. A test case has also been added to verify that BZ #28340 has been fixed. --- Changes from v1: - Assume that all programs with a .dynamic section need a fixup if l_addr != 0. Drop elf_dynamic_info_needs_fixup() to that effect. - Rename tst-ro-dynamic.map to tst-ro-dynamic-mod.map for consistency. elf/Makefile | 11 +++++++++-- elf/dl-load.c | 14 ++++++++++++++ elf/rtld.c | 12 ++++++++++++ elf/tst-ro-dynamic-mod.c | 1 + elf/tst-ro-dynamic-mod.map | 13 +++++++++++++ elf/tst-ro-dynamic.sh | 36 ++++++++++++++++++++++++++++++++++++ 6 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 elf/tst-ro-dynamic-mod.c create mode 100644 elf/tst-ro-dynamic-mod.map create mode 100644 elf/tst-ro-dynamic.sh diff --git a/elf/Makefile b/elf/Makefile index 9f3fadc37e..406bb3ad23 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -223,7 +223,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ tst-tls-ie tst-tls-ie-dlmopen argv0test \ tst-glibc-hwcaps tst-glibc-hwcaps-prepend tst-glibc-hwcaps-mask \ tst-tls20 tst-tls21 tst-dlmopen-dlerror tst-dlmopen-gethostbyname \ - tst-dl-is_dso + tst-dl-is_dso tst-ro-dynamic # reldep9 tests-internal += loadtest unload unload2 circleload1 \ neededtest neededtest2 neededtest3 neededtest4 \ @@ -359,7 +359,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ libmarkermod4-1 libmarkermod4-2 libmarkermod4-3 libmarkermod4-4 \ tst-tls20mod-bad tst-tls21mod tst-dlmopen-dlerror-mod \ tst-auxvalmod \ - tst-dlmopen-gethostbyname-mod \ + tst-dlmopen-gethostbyname-mod tst-ro-dynamic-mod \ # Most modules build with _ISOMAC defined, but those filtered out # depend on internal headers. @@ -1908,3 +1908,10 @@ $(objpfx)tst-getauxval-static.out: $(objpfx)tst-auxvalmod.so tst-getauxval-static-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx) $(objpfx)tst-dlmopen-gethostbyname.out: $(objpfx)tst-dlmopen-gethostbyname-mod.so + +LDFLAGS-tst-ro-dynamic-mod = -Wl,--version-script=tst-ro-dynamic-mod.map +$(objpfx)tst-ro-dynamic-mod.so: tst-ro-dynamic-mod.map +$(objpfx)tst-ro-dynamic.out: tst-ro-dynamic.sh $(objpfx)tst-ro-dynamic-mod.so \ + $(objpfx)ld.so + $(SHELL) $^ '$(test-wrapper-env)' '$(run_program_env)' > $@; \ + $(evaluate-test) diff --git a/elf/dl-load.c b/elf/dl-load.c index 650e4edc35..c33da0467e 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1124,6 +1124,9 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, * executable. Other platforms default to a nonexecutable stack and don't * need PT_GNU_STACK to do so. */ uint_fast16_t stack_flags = DEFAULT_STACK_PERMS; +#ifndef DL_RO_DYN_SECTION + bool readonly_dynamic = false; +#endif { /* Scan the program header table, collecting its load commands. */ @@ -1149,6 +1152,9 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, such a segment to avoid a crash later. */ l->l_ld = (void *) ph->p_vaddr; l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn)); +#ifndef DL_RO_DYN_SECTION + readonly_dynamic = (ph->p_flags & PF_W) == 0; +#endif } break; @@ -1292,6 +1298,14 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, else l->l_ld = (ElfW(Dyn) *) ((ElfW(Addr)) l->l_ld + l->l_addr); +#ifndef DL_RO_DYN_SECTION + if (readonly_dynamic && l->l_addr != 0) + { + errstring = N_("dynamic section of object file not writable"); + goto lose; + } +#endif + elf_get_dynamic_info (l, NULL); /* Make sure we are not dlopen'ing an object that has the diff --git a/elf/rtld.c b/elf/rtld.c index 878e6480f4..d8149f2bca 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -1456,6 +1456,10 @@ dl_main (const ElfW(Phdr) *phdr, /* And it was opened directly. */ ++main_map->l_direct_opencount; +#ifndef DL_RO_DYN_SECTION + bool readonly_dynamic = false; +#endif + /* Scan the program header table for the dynamic section. */ for (ph = phdr; ph < &phdr[phnum]; ++ph) switch (ph->p_type) @@ -1468,6 +1472,9 @@ dl_main (const ElfW(Phdr) *phdr, /* This tells us where to find the dynamic section, which tells us everything we need to do. */ main_map->l_ld = (void *) main_map->l_addr + ph->p_vaddr; +#ifndef DL_RO_DYN_SECTION + readonly_dynamic = (ph->p_flags & PF_W) == 0; +#endif break; case PT_INTERP: /* This "interpreter segment" was used by the program loader to @@ -1612,6 +1619,11 @@ dl_main (const ElfW(Phdr) *phdr, if (! rtld_is_main) { +#ifndef DL_RO_DYN_SECTION + if (readonly_dynamic && main_map->l_addr != 0) + _dl_fatal_printf ("dynamic section of executable is not writable\n"); +#endif + /* Extract the contents of the dynamic section for easy access. */ elf_get_dynamic_info (main_map, NULL); diff --git a/elf/tst-ro-dynamic-mod.c b/elf/tst-ro-dynamic-mod.c new file mode 100644 index 0000000000..2bc87d0c3c --- /dev/null +++ b/elf/tst-ro-dynamic-mod.c @@ -0,0 +1 @@ +int foo = 0; diff --git a/elf/tst-ro-dynamic-mod.map b/elf/tst-ro-dynamic-mod.map new file mode 100644 index 0000000000..dac92e0b94 --- /dev/null +++ b/elf/tst-ro-dynamic-mod.map @@ -0,0 +1,13 @@ +SECTIONS +{ + .data : { *(.data) } :text + .bss : { *(.bss) } :text + .dynamic : { *(.dynamic) } :text :dynamic + .text : { *(.text) } :text +} + +PHDRS +{ + text PT_LOAD FLAGS(5) FILEHDR PHDRS; + dynamic PT_DYNAMIC FLAGS(4); +} diff --git a/elf/tst-ro-dynamic.sh b/elf/tst-ro-dynamic.sh new file mode 100644 index 0000000000..3d7144e0e2 --- /dev/null +++ b/elf/tst-ro-dynamic.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# Ensure ld.so does not crash when verifying DSO with read-only dynamic +# section. +# 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 +# . + +dso=$1 +rtld=$2 +test_wrapper_env=$3 +run_program_env=$4 + +${test_wrapper_env} \ +${run_program_env} \ +$rtld --verify ${dso} + +ret=$? + +# ld.so should fail gracefully by returning 1. +if [ $ret -ne 1 ]; then + echo "ld.so returned $ret" + exit 1 +fi