From patchwork Fri Jul 5 10:43:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 93417 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 74820382EF33 for ; Fri, 5 Jul 2024 10:44:42 +0000 (GMT) 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 [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 9B22A382E6BE for ; Fri, 5 Jul 2024 10:43:44 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 9B22A382E6BE Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 9B22A382E6BE Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1720176226; cv=none; b=Kd115iz8K3/4+3cnlLjR68yt30KO3y8LEOgN4QediVwW69AgGzrnRMQ2qDgmoprR5e3BrpTFi/JXMFsdVOudKArGutaLnl/t2giTRe2OpQtwscoR+3ZDg7aHS8yRrEiO9FAJV/haj4IMeOcLkIwsLBcrYCMtyyFS3IZOSa7zvbA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1720176226; c=relaxed/simple; bh=tOUsUvFcNBciDBtArdYv/GIe9kOAHxwpR3YuWkSc6wg=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=dY1693SGqY4tWkFtYYl2WOW5VcTVR960gfP8xZcy9/XvV0iI+BJewh54zs3ydMIUqYidx2r3VIzYZpAeGeYqGCa0B8pj6xBwGrwFSRmT0CqbaYnbhpNKwfvrge6VwDGw7lp9u3RnRf5Oyx4uH2Ma1Ihtfr+Hg1uvnEeuRiQpzss= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1720176224; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=69HI6V/81dosmDNXIf9XHRYkY0X+/nQkPOoaYN9MgeI=; b=K6ZpWxhlf4LB8Ko7QlarSeWK1hamFHISaCc3g+otiad3bJiqU27QDMORQ5WH5B/Vl01u5S /co9ucbbZalZwGpCk61o9zTRZeJa7W7NidAZsMO7Imt/DbQNvMF+e0biWNa80Pm9UXA6iK CrTn3Pg6MO3a/mqXYQH+EUH7JFvn9eA= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-349-yRcC5KNgMLKGIZRsoBGJ5A-1; Fri, 05 Jul 2024 06:43:42 -0400 X-MC-Unique: yRcC5KNgMLKGIZRsoBGJ5A-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id F2069196A954 for ; Fri, 5 Jul 2024 10:43:41 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.6]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id F0EE73000184 for ; Fri, 5 Jul 2024 10:43:40 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v2 2/2] elf: Handle ld.so with LOAD segment gaps in _dl_find_object (bug 31943) In-Reply-To: Message-ID: <4d4499f86affd74c8409318646ea949e1c445873.1720176100.git.fweimer@redhat.com> References: X-From-Line: 4d4499f86affd74c8409318646ea949e1c445873 Mon Sep 17 00:00:00 2001 Date: Fri, 05 Jul 2024 12:43:37 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org As ld.so is loaded by the kernel, gaps are not filled by the initial mapping. Check the program headers for a gap, and if there is one, handle a non-contiguous ld.so link map similar to a non-contiguous main link map in _dlfo_process_initial. Ideally, the extra code should not be compiled in if the binutils is recent enough and used in a configuration that does not produce gaps. For example, binutils 2.39 and later have commit 9833b7757d246f22db4 ("PR28824, relro security issues"), which should avoid creation of gaps on the x86 architecture. --- elf/dl-find_object.c | 76 ++++++++++++++++++++++++++++---------------- elf/rtld.c | 25 ++++++++++++++- 2 files changed, 72 insertions(+), 29 deletions(-) diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c index 449302eda3..056fbda64c 100644 --- a/elf/dl-find_object.c +++ b/elf/dl-find_object.c @@ -466,6 +466,38 @@ __dl_find_object (void *pc1, struct dl_find_object *result) hidden_def (__dl_find_object) weak_alias (__dl_find_object, _dl_find_object) +/* Subroutine of _dlfo_process_initial to split out noncontigous link + maps. NODELETE is the number of used _dlfo_nodelete_mappings + elements. It is incremented as needed, and the new NODELETE value + is returned. */ +static size_t +_dlfo_process_initial_noncontiguous_map (struct link_map *map, + size_t nodelete) +{ + struct dl_find_object_internal dlfo; + _dl_find_object_from_map (map, &dlfo); + + /* PT_LOAD segments for a non-contiguous link map are added to the + non-closeable mappings. */ + const ElfW(Phdr) *ph = map->l_phdr; + const ElfW(Phdr) *ph_end = map->l_phdr + map->l_phnum; + for (; ph < ph_end; ++ph) + if (ph->p_type == PT_LOAD) + { + if (_dlfo_nodelete_mappings != NULL) + { + /* Second pass only. */ + _dlfo_nodelete_mappings[nodelete] = dlfo; + _dlfo_nodelete_mappings[nodelete].map_start + = ph->p_vaddr + map->l_addr; + _dlfo_nodelete_mappings[nodelete].map_end + = _dlfo_nodelete_mappings[nodelete].map_start + ph->p_memsz; + } + ++nodelete; + } + return nodelete; +} + /* _dlfo_process_initial is called twice. First to compute the array sizes from the initial loaded mappings. Second to fill in the bases and infos arrays with the (still unsorted) data. Returns the @@ -477,29 +509,8 @@ _dlfo_process_initial (void) size_t nodelete = 0; if (!main_map->l_contiguous) - { - struct dl_find_object_internal dlfo; - _dl_find_object_from_map (main_map, &dlfo); - - /* PT_LOAD segments for a non-contiguous are added to the - non-closeable mappings. */ - for (const ElfW(Phdr) *ph = main_map->l_phdr, - *ph_end = main_map->l_phdr + main_map->l_phnum; - ph < ph_end; ++ph) - if (ph->p_type == PT_LOAD) - { - if (_dlfo_nodelete_mappings != NULL) - { - /* Second pass only. */ - _dlfo_nodelete_mappings[nodelete] = dlfo; - _dlfo_nodelete_mappings[nodelete].map_start - = ph->p_vaddr + main_map->l_addr; - _dlfo_nodelete_mappings[nodelete].map_end - = _dlfo_nodelete_mappings[nodelete].map_start + ph->p_memsz; - } - ++nodelete; - } - } + /* Contiguous case already handled in _dl_find_object_init. */ + nodelete = _dlfo_process_initial_noncontiguous_map (main_map, nodelete); size_t loaded = 0; for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns) @@ -511,11 +522,20 @@ _dlfo_process_initial (void) /* lt_library link maps are implicitly NODELETE. */ if (l->l_type == lt_library || l->l_nodelete_active) { - if (_dlfo_nodelete_mappings != NULL) - /* Second pass only. */ - _dl_find_object_from_map - (l, _dlfo_nodelete_mappings + nodelete); - ++nodelete; +#ifdef SHARED + /* The kernel may have loaded ld.so with gaps. */ + if (!l->l_contiguous && l == &GL(dl_rtld_map)) + nodelete + = _dlfo_process_initial_noncontiguous_map (l, nodelete); + else +#endif + { + if (_dlfo_nodelete_mappings != NULL) + /* Second pass only. */ + _dl_find_object_from_map + (l, _dlfo_nodelete_mappings + nodelete); + ++nodelete; + } } else if (l->l_type == lt_loaded) { diff --git a/elf/rtld.c b/elf/rtld.c index 60b0f416e8..cc71f1ded6 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -1286,7 +1286,7 @@ rtld_setup_main_map (struct link_map *main_map) /* Set up the program header information for the dynamic linker itself. It can be accessed via _r_debug and dl_iterate_phdr - callbacks. */ + callbacks, and it is used by _dl_find_object. */ static void rtld_setup_phdr (void) { @@ -1303,6 +1303,29 @@ rtld_setup_phdr (void) GL(dl_rtld_map).l_phdr = rtld_phdr; GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum; + /* The linker may not have produced a contiguous object. The kernel + will load the object with actual gaps (unlike the glibc loader + for shared objects, which always produces a contiguous mapping). + See similar logic in rtld_setup_main_map above. */ + GL(dl_rtld_map).l_contiguous = 1; + { + ElfW(Addr) expected_load_address = 0; + for (const ElfW(Phdr) *ph = rtld_phdr; ph < &rtld_phdr[rtld_ehdr->e_phnum]; + ++ph) + if (ph->p_type == PT_LOAD) + { + ElfW(Addr) mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1); + if (GL(dl_rtld_map).l_contiguous && expected_load_address != 0 + && expected_load_address != mapstart) + GL(dl_rtld_map).l_contiguous = 0; + ElfW(Addr) allocend = ph->p_vaddr + ph->p_memsz; + /* The next expected address is the page following this load + segment. */ + expected_load_address = ((allocend + GLRO(dl_pagesize) - 1) + & ~(GLRO(dl_pagesize) - 1)); + } + } + /* PT_GNU_RELRO is usually the last phdr. */ size_t cnt = rtld_ehdr->e_phnum; while (cnt-- > 0)