Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.2/patches/2224688/?format=api
{ "id": 2224688, "url": "http://patchwork.ozlabs.org/api/1.2/patches/2224688/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/patch/d3f4110e0e542a0b045e278c07573775a4d5a412.1776449736.git.dj@redhat.com/", "project": { "id": 41, "url": "http://patchwork.ozlabs.org/api/1.2/projects/41/?format=api", "name": "GNU C Library", "link_name": "glibc", "list_id": "libc-alpha.sourceware.org", "list_email": "libc-alpha@sourceware.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<d3f4110e0e542a0b045e278c07573775a4d5a412.1776449736.git.dj@redhat.com>", "list_archive_url": null, "date": "2024-02-21T23:47:53", "name": "[v7,2/4] Add system-wide tunables: cache ld.so.cache", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "88973e5fc43295314b8fd4f2d64e5beef6dbc640", "submitter": { "id": 4388, "url": "http://patchwork.ozlabs.org/api/1.2/people/4388/?format=api", "name": "DJ Delorie", "email": "dj@redhat.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/glibc/patch/d3f4110e0e542a0b045e278c07573775a4d5a412.1776449736.git.dj@redhat.com/mbox/", "series": [ { "id": 500385, "url": "http://patchwork.ozlabs.org/api/1.2/series/500385/?format=api", "web_url": "http://patchwork.ozlabs.org/project/glibc/list/?series=500385", "date": "2024-02-21T23:49:50", "name": "Add system-wide tunables", "version": 7, "mbox": "http://patchwork.ozlabs.org/series/500385/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2224688/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2224688/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "libc-alpha@sourceware.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "libc-alpha@sourceware.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=Zj7nO7yx;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)", "sourceware.org;\n\tdkim=pass (1024-bit key,\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=Zj7nO7yx", "sourceware.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com", "sourceware.org; spf=pass smtp.mailfrom=redhat.com", "server2.sourceware.org;\n arc=none smtp.remote-ip=170.10.129.124" ], "Received": [ "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fy3771S0mz1yHp\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 18 Apr 2026 04:19:10 +1000 (AEST)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 479BE4CD202B\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 17 Apr 2026 18:19:08 +0000 (GMT)", "from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.129.124])\n by sourceware.org (Postfix) with ESMTP id 9AD674AA3971\n for <libc-alpha@sourceware.org>; Fri, 17 Apr 2026 18:18:38 +0000 (GMT)", "from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-3-liFwoFSqM6G2ieiNgDstIw-1; Fri,\n 17 Apr 2026 14:18:36 -0400", "from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 13AE41800473\n for <libc-alpha@sourceware.org>; Fri, 17 Apr 2026 18:18:36 +0000 (UTC)", "from greed.delorie.com (unknown [10.22.88.82])\n by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with\n ESMTPS\n id C716030001BE\n for <libc-alpha@sourceware.org>; Fri, 17 Apr 2026 18:18:35 +0000 (UTC)", "from greed.delorie.com (localhost [127.0.0.1])\n by greed.delorie.com (8.16.1/8.16.1) with ESMTPS id 63HIIYO4651993\n (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT)\n for <libc-alpha@sourceware.org>; Fri, 17 Apr 2026 14:18:34 -0400", "(from dj@localhost)\n by greed.delorie.com (8.16.1/8.16.1/Submit) id 63HIIYRP651992;\n Fri, 17 Apr 2026 14:18:34 -0400" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org 479BE4CD202B", "OpenDKIM Filter v2.11.0 sourceware.org 9AD674AA3971" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 9AD674AA3971", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 9AD674AA3971", "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776449918; cv=none;\n b=VCu5KJ8oSrXPwcvxMEFDM54cQkZnZHk66U3Nex8SVCjwspOiCnYKLLycY3esu18HwIPjTtuv1I5tXy8E4IC5+RcSRghm6aGdONnQzeGZNVWKQyfapowLo/HWMYFm+IIzpKRro81+dG4CkUHfk4yvpfXyABPwGquEYIQFvkloYzg=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776449918; c=relaxed/simple;\n bh=JSDsee/mc3CSjDwIfEkZgqbkSxkaybyPLm4A3x1ayEY=;\n h=DKIM-Signature:Message-ID:From:Date:Subject:To;\n b=Q7bc0eIKPuZXDB/klXeVHxZSgPFxWvxMBuZev6QUxm7FX3mw06KXsuy6UctdF4kUj53SqGtwtitfdgL8ZYjvdk+3WRh5oL1v7BbjyP58RVZz01TfrEfu+AIu3mTPjiEpqIqU2IGZkLEIfbhaC4ydDmm+ei7ixoimGBXczrRyZIE=", "ARC-Authentication-Results": "i=1; server2.sourceware.org", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1776449918;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:content-type:content-type:in-reply-to:in-reply-to:\n references:references; bh=/7tNkeJTN+H1zXytnVA6o5hvCzSZSj7b82vrfFdY82o=;\n b=Zj7nO7yx8E55bCU5gDPeWmu2BxauZwQswWunmpdhmjAkErttbWIpz0GFCA6QJd7Uc7Jzcm\n np7RmuWG6v/oah+K4jBtHE0mpqm15LXWkONAPwaX68LKeHpkGz7rvrdxsXNOZmPLf14mZJ\n nGav7GwQsMZWS8Rk1BZOQNTViryT3QA=", "X-MC-Unique": "liFwoFSqM6G2ieiNgDstIw-1", "X-Mimecast-MFC-AGG-ID": "liFwoFSqM6G2ieiNgDstIw_1776449916", "Message-ID": "\n <d3f4110e0e542a0b045e278c07573775a4d5a412.1776449736.git.dj@redhat.com>", "In-Reply-To": "<cover.1776449736.git.dj@redhat.com>", "References": "<cover.1776449736.git.dj@redhat.com>", "From": "DJ Delorie <dj@redhat.com>", "Date": "Wed, 21 Feb 2024 18:47:53 -0500", "Subject": "[PATCH v7 2/4] Add system-wide tunables: cache ld.so.cache", "To": "libc-alpha@sourceware.org", "X-Scanned-By": "MIMEDefang 3.4.1 on 10.30.177.4", "X-Mimecast-Spam-Score": "2", "X-Mimecast-MFC-PROC-ID": "LuTh8ysrjBN27ktPSTO1gTVSfQ8CAwZBHsnd3INj2FI_1776449916", "X-Mimecast-Originator": "redhat.com", "Content-type": "text/plain; charset=UTF-8", "X-BeenThere": "libc-alpha@sourceware.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "Libc-alpha mailing list <libc-alpha.sourceware.org>", "List-Unsubscribe": "<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>", "List-Archive": "<https://sourceware.org/pipermail/libc-alpha/>", "List-Post": "<mailto:libc-alpha@sourceware.org>", "List-Help": "<mailto:libc-alpha-request@sourceware.org?subject=help>", "List-Subscribe": "<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>", "Errors-To": "libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org" }, "content": "The purpose of this change is twofold:\n\n1. The ld.so.cache is cached in memory and only re-read if/when\n it changes on disk. This allows us to have much more intensive\n security checks in the future, without impacting performance as\n much. It also allows for cases where the cache is corrupted -\n we continue using the last valid one.\n\n2. We break out the load/check logic so that the cache can be\n loaded independently of the library lookup, such as for\n code that only needs to look at the extensions.\n---\n elf/Makefile | 3 +\n elf/dl-cache.c | 265 ++++++++++++------\n elf/tst-ldconfig-cache.c | 134 +++++++++\n elf/tst-ldconfig-cache.root/etc/ld.so.conf | 3 +\n elf/tst-ldconfig-cache.root/ldconfig.req | 0\n .../tst-ldconfig-cache.script | 7 +\n 6 files changed, 324 insertions(+), 88 deletions(-)\n create mode 100644 elf/tst-ldconfig-cache.c\n create mode 100644 elf/tst-ldconfig-cache.root/etc/ld.so.conf\n create mode 100644 elf/tst-ldconfig-cache.root/ldconfig.req\n create mode 100644 elf/tst-ldconfig-cache.root/tst-ldconfig-cache.script", "diff": "diff --git a/elf/Makefile b/elf/Makefile\nindex 6b366df91b..5398fe0d2c 100644\n--- a/elf/Makefile\n+++ b/elf/Makefile\n@@ -559,6 +559,7 @@ endif\n tests-container += \\\n tst-dlopen-self-container \\\n tst-dlopen-tlsmodid-container \\\n+ tst-ldconfig-cache \\\n tst-pldd \\\n tst-preload-pthread-libc \\\n tst-rootdir \\\n@@ -714,6 +715,8 @@ one-hundred = $(foreach x,0 1 2 3 4 5 6 7 8 9, \\\n 0$x 1$x 2$x 3$x 4$x 5$x 6$x 7$x 8$x 9$x)\n tst-tls-many-dynamic-modules := \\\n $(foreach n,$(one-hundred),tst-tls-manydynamic$(n)mod)\n+tst-ldconfig-cache-modules := \\\n+ $(foreach n,1 2 3 4 5,tst-tls-manydynamic$(n)mod)\n tst-tls-many-dynamic-modules-dep-suffixes = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 \\\n \t\t\t\t\t 14 15 16 17 18 19\n tst-tls-many-dynamic-modules-dep = \\\ndiff --git a/elf/dl-cache.c b/elf/dl-cache.c\nindex 9458ffae2a..9d5fc05cd6 100644\n--- a/elf/dl-cache.c\n+++ b/elf/dl-cache.c\n@@ -25,11 +25,21 @@\n #include <_itoa.h>\n #include <dl-hwcaps.h>\n #include <dl-isa-level.h>\n+#include <fcntl.h>\n+#include <sys/stat.h>\n \n /* This is the starting address and the size of the mmap()ed file. */\n static struct cache_file *cache;\n static struct cache_file_new *cache_new;\n static size_t cachesize;\n+static struct cache_extension_all_loaded ext;\n+\n+static struct {\n+ typeof ((*(struct __stat64_t64 *)0).st_mtime) mtime;\n+ typeof ((*(struct __stat64_t64 *)0).st_ino) ino;\n+ typeof ((*(struct __stat64_t64 *)0).st_size) size;\n+ typeof ((*(struct __stat64_t64 *)0).st_dev) dev;\n+} cache_file_time, new_cache_file_time;\n \n #ifdef SHARED\n /* This is used to cache the priorities of glibc-hwcaps\n@@ -52,6 +62,7 @@ glibc_hwcaps_priorities_free (void)\n free (glibc_hwcaps_priorities);\n glibc_hwcaps_priorities = NULL;\n glibc_hwcaps_priorities_allocated = 0;\n+ glibc_hwcaps_priorities_length = 0;\n }\n \n /* Ordered comparison of a hwcaps string from the cache on the left\n@@ -83,10 +94,6 @@ glibc_hwcaps_compare (uint32_t left_index, struct dl_hwcaps_priority *right)\n static void\n glibc_hwcaps_priorities_init (void)\n {\n- struct cache_extension_all_loaded ext;\n- if (!cache_extension_load (cache_new, cache, cachesize, &ext))\n- return;\n-\n uint32_t length = (ext.sections[cache_extension_tag_glibc_hwcaps].size\n \t\t / sizeof (uint32_t));\n if (length > glibc_hwcaps_priorities_allocated)\n@@ -373,6 +380,162 @@ _dl_cache_libcmp (const char *p1, const char *p2)\n return *p1 - *p2;\n }\n \n+/* Set the cache back to the \"no cache\" state, which may include\n+ cleaning up a loaded cache. */\n+static void\n+_dl_maybe_unload_ldsocache (void)\n+{\n+ if (cache != NULL)\n+ __munmap (cache, cachesize);\n+\n+ cache = NULL;\n+ cache_new = NULL;\n+ cachesize = 0;\n+\n+#ifdef SHARED\n+ glibc_hwcaps_priorities_free ();\n+#endif\n+}\n+\n+/* Returns TRUE if for any reason the cache needs to be reloaded\n+ (including, the first time, loaded). */\n+static bool\n+_dl_check_ldsocache_needs_loading (void)\n+{\n+ int rv;\n+ static bool copy_old_time = 0;\n+ struct __stat64_t64 new_cache_file_stat;\n+\n+ /* Save the previous stat every time. We only care when this\n+ changes, and we only stat it here, so we can get away with doing\n+ the copy now instead of at every single return statement in this\n+ function. However, we only need to copy it if the previous stat\n+ succeeded. The only way this could be subverted is if the admin\n+ moves the file aside, then moves it back, but CACHE would be set\n+ to NULL in the interim so that would be detected. */\n+ if (copy_old_time)\n+ cache_file_time = new_cache_file_time;\n+ rv = __fstatat64_time64 (AT_FDCWD, LD_SO_CACHE, &new_cache_file_stat, 0);\n+ copy_old_time = (rv >= 0);\n+\n+ /* No file to load, but there used to be. Assume user intentionally\n+ deleted the cache and act accordingly. */\n+ if (rv < 0 && cache != NULL)\n+ {\n+ _dl_maybe_unload_ldsocache ();\n+ return false;\n+ }\n+\n+ /* No file to load and no loaded cache, so nothing to do. */\n+ if (rv < 0)\n+ return false;\n+\n+ /* Any file is better than no file (likely the first time\n+ through). */\n+ if (cache == NULL)\n+ return true;\n+\n+ /* Store the fields we check, in order they're likely to differ. */\n+ new_cache_file_time.mtime = new_cache_file_stat.st_mtime;\n+ new_cache_file_time.ino = new_cache_file_stat.st_ino;\n+ new_cache_file_time.size = new_cache_file_stat.st_size;\n+ new_cache_file_time.dev = new_cache_file_stat.st_dev;\n+\n+ /* At this point, NEW_CACHE_FILE_TIME is valid as well as\n+ CACHE_FILE_TIME, so we compare them. */\n+ return (memcmp (&new_cache_file_time, &cache_file_time,\n+\t\t sizeof(new_cache_file_time)));\n+}\n+\n+/* Attemps to load and validate the cache. On return, CACHE is either\n+ unchanged (still loaded or still not loaded) or valid. */\n+static void\n+_dl_maybe_load_ldsocache (void)\n+{\n+ struct cache_file *tmp_cache = NULL;\n+ struct cache_file_new *tmp_cache_new = NULL;\n+ size_t tmp_cachesize = 0;\n+ \n+ /* Read the contents of the file. */\n+ void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &tmp_cachesize,\n+\t\t\t\t\t PROT_READ);\n+\n+ /* We can handle three different cache file formats here:\n+ - only the new format\n+ - the old libc5/glibc2.0/2.1 format\n+ - the old format with the new format in it\n+ The following checks if the cache contains any of these formats. */\n+ if (file != MAP_FAILED && tmp_cachesize > sizeof *cache_new\n+ && memcmp (file, CACHEMAGIC_VERSION_NEW,\n+\t\t sizeof CACHEMAGIC_VERSION_NEW - 1) == 0\n+ /* Check for corruption, avoiding overflow. */\n+ && ((tmp_cachesize - sizeof *cache_new) / sizeof (struct file_entry_new)\n+\t >= ((struct cache_file_new *) file)->nlibs))\n+ {\n+ if (! cache_file_new_matches_endian (file))\n+\t{\n+\t __munmap (file, tmp_cachesize);\n+\t return;\n+\t}\n+\n+ tmp_cache_new = file;\n+ tmp_cache = file;\n+ }\n+ else if (file != MAP_FAILED && tmp_cachesize > sizeof *cache\n+\t && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0\n+\t /* Check for corruption, avoiding overflow. */\n+\t && ((tmp_cachesize - sizeof *cache) / sizeof (struct file_entry)\n+\t >= ((struct cache_file *) file)->nlibs))\n+ {\n+ size_t offset;\n+ /* Looks ok. */\n+ tmp_cache = file;\n+\n+ /* Check for new version. */\n+ offset = ALIGN_CACHE (sizeof (struct cache_file)\n+\t\t\t + cache->nlibs * sizeof (struct file_entry));\n+\n+ tmp_cache_new = (struct cache_file_new *) ((void *) tmp_cache + offset);\n+ if (tmp_cachesize < (offset + sizeof (struct cache_file_new))\n+\t || memcmp (tmp_cache_new->magic, CACHEMAGIC_VERSION_NEW,\n+\t\t sizeof CACHEMAGIC_VERSION_NEW - 1) != 0)\n+\ttmp_cache_new = NULL;\n+ else\n+\t{\n+\t if (! cache_file_new_matches_endian (tmp_cache_new))\n+\t /* The old-format part of the cache is bogus as well\n+\t if the endianness does not match. (But it is\n+\t unclear how the new header can be located if the\n+\t endianness does not match.) */\n+\t {\n+\t __munmap (file, tmp_cachesize);\n+\t return;\n+\t }\n+\t}\n+ }\n+ else\n+ {\n+ if (file != MAP_FAILED)\n+\t__munmap (file, tmp_cachesize);\n+ return;\n+ }\n+\n+ struct cache_extension_all_loaded tmp_ext;\n+ if (!cache_extension_load (tmp_cache_new, tmp_cache, tmp_cachesize, &tmp_ext))\n+ /* The extension is corrupt, so the cache is corrupt. */\n+ return;\n+\n+ /* If we've gotten here, the loaded cache is good and we need to\n+ save it. */\n+ _dl_maybe_unload_ldsocache ();\n+ cache = tmp_cache;\n+ cache_new = tmp_cache_new;\n+ cachesize = tmp_cachesize;\n+ ext = tmp_ext;\n+\n+ assert (cache != NULL);\n+}\n+\n \n /* Look up NAME in ld.so.cache and return the file name stored there, or null\n if none is found. The cache is loaded if it was not already. If loading\n@@ -388,81 +551,14 @@ _dl_load_cache_lookup (const char *name)\n if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))\n _dl_debug_printf (\" search cache=%s\\n\", LD_SO_CACHE);\n \n- if (cache == NULL)\n- {\n- /* Read the contents of the file. */\n- void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,\n-\t\t\t\t\t PROT_READ);\n-\n- /* We can handle three different cache file formats here:\n-\t - only the new format\n-\t - the old libc5/glibc2.0/2.1 format\n-\t - the old format with the new format in it\n-\t The following checks if the cache contains any of these formats. */\n- if (file != MAP_FAILED && cachesize > sizeof *cache_new\n-\t && memcmp (file, CACHEMAGIC_VERSION_NEW,\n-\t\t sizeof CACHEMAGIC_VERSION_NEW - 1) == 0\n-\t /* Check for corruption, avoiding overflow. */\n-\t && ((cachesize - sizeof *cache_new) / sizeof (struct file_entry_new)\n-\t >= ((struct cache_file_new *) file)->nlibs))\n-\t{\n-\t if (! cache_file_new_matches_endian (file))\n-\t {\n-\t __munmap (file, cachesize);\n-\t file = (void *) -1;\n-\t }\n-\t cache_new = file;\n-\t cache = file;\n-\t}\n- else if (file != MAP_FAILED && cachesize > sizeof *cache\n-\t && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0\n-\t /* Check for corruption, avoiding overflow. */\n-\t && ((cachesize - sizeof *cache) / sizeof (struct file_entry)\n-\t\t >= ((struct cache_file *) file)->nlibs))\n-\t{\n-\t size_t offset;\n-\t /* Looks ok. */\n-\t cache = file;\n-\n-\t /* Check for new version. */\n-\t offset = ALIGN_CACHE (sizeof (struct cache_file)\n-\t\t\t\t+ cache->nlibs * sizeof (struct file_entry));\n-\n-\t cache_new = (struct cache_file_new *) ((void *) cache + offset);\n-\t if (cachesize < (offset + sizeof (struct cache_file_new))\n-\t || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW,\n-\t\t\t sizeof CACHEMAGIC_VERSION_NEW - 1) != 0)\n-\t cache_new = (void *) -1;\n-\t else\n-\t {\n-\t if (! cache_file_new_matches_endian (cache_new))\n-\t\t{\n-\t\t /* The old-format part of the cache is bogus as well\n-\t\t if the endianness does not match. (But it is\n-\t\t unclear how the new header can be located if the\n-\t\t endianness does not match.) */\n-\t\t cache = (void *) -1;\n-\t\t cache_new = (void *) -1;\n-\t\t __munmap (file, cachesize);\n-\t\t}\n-\t }\n-\t}\n- else\n-\t{\n-\t if (file != MAP_FAILED)\n-\t __munmap (file, cachesize);\n-\t cache = (void *) -1;\n-\t}\n+ if (_dl_check_ldsocache_needs_loading ())\n+ _dl_maybe_load_ldsocache ();\n \n- assert (cache != NULL);\n- }\n-\n- if (cache == (void *) -1)\n- /* Previously looked for the cache file and didn't find it. */\n+ if (cache == NULL)\n return NULL;\n \n const char *best;\n- if (cache_new != (void *) -1)\n+ if (cache_new != NULL)\n {\n const char *string_table = (const char *) cache_new;\n best = search_cache (string_table, cachesize,\n@@ -487,10 +583,10 @@ _dl_load_cache_lookup (const char *name)\n if (best == NULL)\n return NULL;\n \n- /* The double copy is *required* since malloc may be interposed\n- and call dlopen itself whose completion would unmap the data\n- we are accessing. Therefore we must make the copy of the\n- mapping data without using malloc. */\n+ /* The double copy is required since malloc may be interposed and\n+ call dlopen itself whose completion may unmap the data we are\n+ accessing. Therefore we must make the copy of the mapping data\n+ without using malloc. */\n char *temp;\n size_t best_len = strlen (best) + 1;\n temp = alloca (best_len);\n@@ -506,14 +602,7 @@ _dl_load_cache_lookup (const char *name)\n void\n _dl_unload_cache (void)\n {\n- if (cache != NULL && cache != (struct cache_file *) -1)\n- {\n- __munmap (cache, cachesize);\n- cache = NULL;\n- }\n-#ifdef SHARED\n- /* This marks the glibc_hwcaps_priorities array as out-of-date. */\n- glibc_hwcaps_priorities_length = 0;\n-#endif\n+ /* Functionality is no longer needed, but kept for internal ABI for\n+ now. */\n }\n #endif\ndiff --git a/elf/tst-ldconfig-cache.c b/elf/tst-ldconfig-cache.c\nnew file mode 100644\nindex 0000000000..9f71418b3a\n--- /dev/null\n+++ b/elf/tst-ldconfig-cache.c\n@@ -0,0 +1,134 @@\n+/* Test ldconfig cache is correctly used when changed.\n+ Copyright (C) 2026 Free Software Foundation, Inc.\n+ This file is part of the GNU C Library.\n+\n+ The GNU C Library is free software; you can redistribute it and/or\n+ modify it under the terms of the GNU Lesser General Public License as\n+ published by the Free Software Foundation; either version 2.1 of the\n+ License, or (at your option) any later version.\n+\n+ The GNU C Library is distributed in the hope that it will be useful,\n+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n+ Lesser General Public License for more details.\n+\n+ You should have received a copy of the GNU Lesser General Public\n+ License along with the GNU C Library; see the file COPYING.LIB. If\n+ not, see <https://www.gnu.org/licenses/>. */\n+\n+/* What we're testing for: We initially load ld.so.cache at startup\n+ and remember it. If we detect that ld.so.cache has changed, and we\n+ can load it successfully, we replace our remember it. If it\n+ doesn't change, or if the new version is corrupted, we continue\n+ using the old remembered copy. */\n+\n+#include <fcntl.h>\n+\n+#include <support/support.h>\n+#include <support/check.h>\n+\n+#include <support/xstdio.h>\n+#include <support/xstdlib.h>\n+#include <support/xdlfcn.h>\n+#include <support/xunistd.h>\n+\n+\n+/* Verify that we can (or can't) load one of our test objects. */\n+static void\n+try (int i, int invert)\n+{\n+ char dlname[100];\n+ char symname[100];\n+ int (*proc)(int);\n+ void *dl;\n+\n+ /* These match the objects copied by tst-ldconfig-cache.script,\n+ copied from tst-tls-manydynamic*.so. */\n+ sprintf (dlname, \"libcache%d.so\", i);\n+ sprintf (symname, \"set_value_%02d\", i);\n+\n+ dl = dlopen (dlname, RTLD_NOW);\n+\n+ if (invert)\n+ {\n+ /* This is a negative test; if the object doesn't load the test\n+\t passes. */\n+ TEST_VERIFY (dl == NULL);\n+ return;\n+ }\n+ else\n+ {\n+ /* This is a positive test; if the object doesn't load the test\n+\t fails. */\n+ if (dl == NULL)\n+\tFAIL_EXIT1 (\"error: dlopen: %s\\n\", dlerror ());\n+ }\n+\n+ proc = xdlsym (dl, symname);\n+ /* We don't need to call the symbol, just make sure it exists. */\n+ TEST_VERIFY (proc != NULL);\n+\n+ xdlclose (dl);\n+}\n+\n+/* Cause corruption in the cache that should prevent loading it. */\n+static void\n+corrupt (void)\n+{\n+ int fd = xopen (\"/etc/ld.so.cache\", O_RDWR, 0);\n+ char bytes[] = { 15, 32, 184, 4 };\n+ xwrite (fd, bytes, sizeof(bytes));\n+ xclose (fd);\n+}\n+\n+/* Regenerate the cache from ld.so.conf. */\n+static void\n+ldconfig (void)\n+{\n+ xsystem (\"/sbin/ldconfig -X\");\n+}\n+\n+/* Change ld.so.conf to refer to the new directory, and generate a new\n+ cache. */\n+static void\n+newpath (const char *p)\n+{\n+ FILE *f = xfopen (\"/etc/ld.so.conf\", \"w\");\n+ fprintf (f, \"%s\\n\", p);\n+ xfclose (f);\n+\n+ ldconfig ();\n+}\n+\n+static int\n+do_test (void)\n+{\n+ /* Test that the cache we started with can still load objects in\n+ /a. */\n+ try (1, 0);\n+\n+ /* Create a new cache that doesn't include /a but corrupt it. Test\n+ that we still use the cache with /a in it. */\n+ newpath (\"/c\");\n+ corrupt ();\n+ try (2, 0);\n+\n+ /* Regenerate a clean cache with /a in it and verify we can load\n+ objects in /a. */\n+ newpath (\"/a\");\n+ try (3, 0);\n+\n+ /* Generate a new cache with /b but not /a and make sure objects\n+ in /a can't be loaded. */\n+ newpath (\"/b\");\n+ try (3, 1);\n+\n+ /* But objects in /b can be loaded. */\n+ try (4, 0);\n+ /* Even multiple times. */\n+ try (5, 0);\n+\n+ return 0;\n+}\n+\n+#include <support/test-driver.c>\ndiff --git a/elf/tst-ldconfig-cache.root/etc/ld.so.conf b/elf/tst-ldconfig-cache.root/etc/ld.so.conf\nnew file mode 100644\nindex 0000000000..2b4c2d7817\n--- /dev/null\n+++ b/elf/tst-ldconfig-cache.root/etc/ld.so.conf\n@@ -0,0 +1,3 @@\n+/lib\n+/lib64\n+/a\ndiff --git a/elf/tst-ldconfig-cache.root/ldconfig.req b/elf/tst-ldconfig-cache.root/ldconfig.req\nnew file mode 100644\nindex 0000000000..e69de29bb2\ndiff --git a/elf/tst-ldconfig-cache.root/tst-ldconfig-cache.script b/elf/tst-ldconfig-cache.root/tst-ldconfig-cache.script\nnew file mode 100644\nindex 0000000000..4f258cac1e\n--- /dev/null\n+++ b/elf/tst-ldconfig-cache.root/tst-ldconfig-cache.script\n@@ -0,0 +1,7 @@\n+mkdirp 0755 /a\n+cp $B/elf/tst-tls-manydynamic01mod.so /a/libcache1.so\n+cp $B/elf/tst-tls-manydynamic02mod.so /a/libcache2.so\n+cp $B/elf/tst-tls-manydynamic03mod.so /a/libcache3.so\n+mkdirp 0755 /b\n+cp $B/elf/tst-tls-manydynamic04mod.so /b/libcache4.so\n+cp $B/elf/tst-tls-manydynamic05mod.so /b/libcache5.so\n", "prefixes": [ "v7", "2/4" ] }