From patchwork Tue May 20 09:25:54 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 112652 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 14B4A3858C42 for ; Tue, 20 May 2025 09:42:52 +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 ESMTP id 4E052385841E for ; Tue, 20 May 2025 09:26:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 4E052385841E Authentication-Results: sourceware.org; dmarc=pass (p=quarantine 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 4E052385841E 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=1747733163; cv=none; b=myISoWcefrF8ZPUjnQEO0aOVnQitdmnIXIYFvPQ5FzlBXph4NB/cfE7CWdurcR0Y3g5cO1sdSAFd2s9UmDiU3OwS0kreFf70kR//N4aZEaEhoX7xiP7HKq9STsZ52mUiz1dcZ7BWC3pvVbipGKttOY/SPToFTGzt9d5L+TEmp/E= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1747733163; c=relaxed/simple; bh=mc7eJ8FiOnboDATUI/5vJeExPB68v+acDM38D/n91Vk=; h=DKIM-Signature:From:To:Subject:Message-ID:Date:MIME-Version; b=BNlqONFeTKQbqS188H9I4S1mouJjA/C+fA4wd448phJTSaNdCMaNhAAaTMqanZsbELDTw1xaQ5PtizcGjQOWch3LtrKJbiccz4lugb+VOs0xWlVHyZSV48OhKY2Bj6MQSVUzFXryZ5PaKYBwY759G86VrsNHsatshiORXfQcv0s= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1747733161; 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=JksVPRvh6qUKbo93MYOBpF5oP93a542JXgYC8SNOoJ0=; b=HsyPAUnCUjPCGpfri4BNEp+vU3kIAUWNWWLmGwZUsD6lf+gfCZ0dxFTd4xSJDxTopVnY05 24WZ35Fa6LnORwBINwEwU04URDfwKJGB5AloCJ/isQSvwHPzWMhnqqHEV7mKQ+ztGfT2NK zlbtVd5gI1bQRDPNCwZiGOGeY0MNc5E= Received: from mx-prod-mc-05.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-49-mB3hbdNjOK2By-G1zDDZEA-1; Tue, 20 May 2025 05:25:59 -0400 X-MC-Unique: mB3hbdNjOK2By-G1zDDZEA-1 X-Mimecast-MFC-AGG-ID: mB3hbdNjOK2By-G1zDDZEA_1747733159 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (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-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E5EAF195608A for ; Tue, 20 May 2025 09:25:58 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.2.16.247]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 050A119560AE for ; Tue, 20 May 2025 09:25:57 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH v6 23/23] elf: Handle ld.so with LOAD segment gaps in _dl_find_object (bug 31943) In-Reply-To: Message-ID: References: X-From-Line: f7b5097ae3093fcddb058b1437b087ef06e0fa19 Mon Sep 17 00:00:00 2001 Date: Tue, 20 May 2025 11:25:54 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 2aqhwxdbep7ersNiG-y2AEPc-8UjMfa1VvUcvka2cHk_1747733159 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.1 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_H5, RCVD_IN_MSPIKE_WL, RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED, 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 If the architecture supports multiple page sizes, compile in support code that checks for gaps between load segments. Otherwise, assume that ld does not produce such binaries. --- elf/dl-find_object.c | 77 ++++++++++++++++++++++++++++---------------- elf/rtld.c | 28 +++++++++++++++- 2 files changed, 76 insertions(+), 29 deletions(-) diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c index 1e76373292..389b20c70d 100644 --- a/elf/dl-find_object.c +++ b/elf/dl-find_object.c @@ -26,6 +26,7 @@ #include #include #include +#include /* Fallback implementation of _dl_find_object. It uses a linear search, needs locking, and is not async-signal-safe. It is used in @@ -465,6 +466,38 @@ _dl_find_object (void *pc1, struct dl_find_object *result) } rtld_hidden_def (_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 @@ -476,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) @@ -510,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; +#if defined SHARED && PAGE_SIZE_MIN != PAGE_SIZE_MAX + /* The kernel may have loaded ld.so with gaps. */ + if (!l->l_contiguous && l == &_dl_rtld_map) + nodelete + = _dlfo_process_initial_noncontiguous_map (l, nodelete); + else +#endif /* Multiple page sizes. */ + { + 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 7301c80a13..0847a8f1a3 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -52,6 +52,7 @@ #include #include #include +#include #include @@ -1240,7 +1241,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) { @@ -1258,6 +1259,31 @@ rtld_setup_phdr (void) _dl_rtld_map.l_phnum = rtld_ehdr->e_phnum; + _dl_rtld_map.l_contiguous = 1; +#if PAGE_SIZE_MIN != PAGE_SIZE_MAX + /* 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. */ + { + 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 (_dl_rtld_map.l_contiguous && expected_load_address != 0 + && expected_load_address != mapstart) + _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)); + } + } +#endif /* Support for multiple page sizes. */ + /* PT_GNU_RELRO is usually the last phdr. */ size_t cnt = rtld_ehdr->e_phnum; while (cnt-- > 0)