Handle load segment gaps in _dl_find_object (RHEL-104852)
Resolves: RHEL-104852
This commit is contained in:
parent
598d4ec8e6
commit
433a7d6f10
106
glibc-RHEL-104852-1.patch
Normal file
106
glibc-RHEL-104852-1.patch
Normal file
@ -0,0 +1,106 @@
|
||||
commit 2cac9559e06044ba520e785c151fbbd25011865f
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Aug 1 10:20:23 2025 +0200
|
||||
|
||||
elf: Extract rtld_setup_phdr function from dl_main
|
||||
|
||||
Remove historic binutils reference from comment and update
|
||||
how this data is used by applications.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
Conflicts:
|
||||
elf/rtld.
|
||||
(missing __ehdr_start cleanup downstream)
|
||||
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index 667880e18ae816d8..a9073d4e14b07410 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -1306,6 +1306,45 @@ rtld_setup_main_map (struct link_map *main_map)
|
||||
return has_interp;
|
||||
}
|
||||
|
||||
+/* Set up the program header information for the dynamic linker
|
||||
+ itself. It can be accessed via _r_debug and dl_iterate_phdr
|
||||
+ callbacks. */
|
||||
+static void
|
||||
+rtld_setup_phdr (void)
|
||||
+{
|
||||
+ const ElfW(Ehdr) *rtld_ehdr;
|
||||
+
|
||||
+ /* Starting from binutils-2.23, the linker will define the magic symbol
|
||||
+ __ehdr_start to point to our own ELF header if it is visible in a
|
||||
+ segment that also includes the phdrs. If that's not available, we use
|
||||
+ the old method that assumes the beginning of the file is part of the
|
||||
+ lowest-addressed PT_LOAD segment. */
|
||||
+#ifdef HAVE_EHDR_START
|
||||
+ extern const ElfW(Ehdr) __ehdr_start __attribute__ ((visibility ("hidden")));
|
||||
+ rtld_ehdr = &__ehdr_start;
|
||||
+#else
|
||||
+ rtld_ehdr = (void *) GL(dl_rtld_map).l_map_start;
|
||||
+#endif
|
||||
+ assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr);
|
||||
+ assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr)));
|
||||
+
|
||||
+ const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff;
|
||||
+
|
||||
+ GL(dl_rtld_map).l_phdr = rtld_phdr;
|
||||
+ GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum;
|
||||
+
|
||||
+
|
||||
+ /* PT_GNU_RELRO is usually the last phdr. */
|
||||
+ size_t cnt = rtld_ehdr->e_phnum;
|
||||
+ while (cnt-- > 0)
|
||||
+ if (rtld_phdr[cnt].p_type == PT_GNU_RELRO)
|
||||
+ {
|
||||
+ GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr;
|
||||
+ GL(dl_rtld_map).l_relro_size = rtld_phdr[cnt].p_memsz;
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/* Adjusts the contents of the stack and related globals for the user
|
||||
entry point. The ld.so processed skip_args arguments and bumped
|
||||
_dl_argv and _dl_argc accordingly. Those arguments are removed from
|
||||
@@ -1790,39 +1829,7 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
if (GLRO(dl_use_load_bias) == (ElfW(Addr)) -2)
|
||||
GLRO(dl_use_load_bias) = main_map->l_addr == 0 ? -1 : 0;
|
||||
|
||||
- /* Set up the program header information for the dynamic linker
|
||||
- itself. It is needed in the dl_iterate_phdr callbacks. */
|
||||
- const ElfW(Ehdr) *rtld_ehdr;
|
||||
-
|
||||
- /* Starting from binutils-2.23, the linker will define the magic symbol
|
||||
- __ehdr_start to point to our own ELF header if it is visible in a
|
||||
- segment that also includes the phdrs. If that's not available, we use
|
||||
- the old method that assumes the beginning of the file is part of the
|
||||
- lowest-addressed PT_LOAD segment. */
|
||||
-#ifdef HAVE_EHDR_START
|
||||
- extern const ElfW(Ehdr) __ehdr_start __attribute__ ((visibility ("hidden")));
|
||||
- rtld_ehdr = &__ehdr_start;
|
||||
-#else
|
||||
- rtld_ehdr = (void *) GL(dl_rtld_map).l_map_start;
|
||||
-#endif
|
||||
- assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr);
|
||||
- assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr)));
|
||||
-
|
||||
- const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff;
|
||||
-
|
||||
- GL(dl_rtld_map).l_phdr = rtld_phdr;
|
||||
- GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum;
|
||||
-
|
||||
-
|
||||
- /* PT_GNU_RELRO is usually the last phdr. */
|
||||
- size_t cnt = rtld_ehdr->e_phnum;
|
||||
- while (cnt-- > 0)
|
||||
- if (rtld_phdr[cnt].p_type == PT_GNU_RELRO)
|
||||
- {
|
||||
- GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr;
|
||||
- GL(dl_rtld_map).l_relro_size = rtld_phdr[cnt].p_memsz;
|
||||
- break;
|
||||
- }
|
||||
+ rtld_setup_phdr ();
|
||||
|
||||
/* Add the dynamic linker to the TLS list if it also uses TLS. */
|
||||
if (GL(dl_rtld_map).l_tls_blocksize != 0)
|
||||
457
glibc-RHEL-104852-2.patch
Normal file
457
glibc-RHEL-104852-2.patch
Normal file
@ -0,0 +1,457 @@
|
||||
commit 20681be149b9eb1b6c1f4246bf4bd801221c86cd
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Aug 1 10:20:23 2025 +0200
|
||||
|
||||
elf: Handle ld.so with LOAD segment gaps in _dl_find_object (bug 31943)
|
||||
|
||||
Detect if ld.so not contiguous and handle that case in _dl_find_object.
|
||||
Set l_find_object_processed even for initially loaded link maps,
|
||||
otherwise dlopen of an initially loaded object adds it to
|
||||
_dlfo_loaded_mappings (where maps are expected to be contiguous),
|
||||
in addition to _dlfo_nodelete_mappings.
|
||||
|
||||
Test elf/tst-link-map-contiguous-ldso iterates over the loader
|
||||
image, reading every word to make sure memory is actually mapped.
|
||||
It only does that if the l_contiguous flag is set for the link map.
|
||||
Otherwise, it finds gaps with mmap and checks that _dl_find_object
|
||||
does not return the ld.so mapping for them.
|
||||
|
||||
The test elf/tst-link-map-contiguous-main does the same thing for
|
||||
the libc.so shared object. This only works if the kernel loaded
|
||||
the main program because the glibc dynamic loader may fill
|
||||
the gaps with PROT_NONE mappings in some cases, making it contiguous,
|
||||
but accesses to individual words may still fault.
|
||||
|
||||
Test elf/tst-link-map-contiguous-libc is again slightly different
|
||||
because the dynamic loader always fills the gaps with PROT_NONE
|
||||
mappings, so a different form of probing has to be used.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
Conflicts:
|
||||
elf/dl-find_object.h
|
||||
(missing SFrame support downstream. Update
|
||||
l_find_object_processed early due to the possible
|
||||
return from the for loop).
|
||||
elf/dl-find_object.c
|
||||
(missing is_rtld_link_map downstream)
|
||||
elf/rtld.c
|
||||
(missing GL(dl_rtld_map) refactoring downstream)
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index b37636c0f865f4e6..190ee83120c498a3 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -519,6 +519,8 @@ tests-internal += \
|
||||
tst-dl-hwcaps_split \
|
||||
tst-dlmopen2 \
|
||||
tst-hash-collision3 \
|
||||
+ tst-link-map-contiguous-ldso \
|
||||
+ tst-link-map-contiguous-libc \
|
||||
tst-ptrguard1 \
|
||||
tst-stackguard1 \
|
||||
tst-tls-surplus \
|
||||
@@ -531,6 +533,10 @@ tests-internal += \
|
||||
tst-dl_find_object tst-dl_find_object-threads \
|
||||
# tests-internal
|
||||
|
||||
+ifeq ($(build-hardcoded-path-in-tests),yes)
|
||||
+tests-internal += tst-link-map-contiguous-main
|
||||
+endif
|
||||
+
|
||||
tests-container += \
|
||||
tst-dlopen-self-container \
|
||||
tst-dlopen-tlsmodid-container \
|
||||
diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c
|
||||
index 62a8a61f6b6032cf..128329aa20ef9ed6 100644
|
||||
--- a/elf/dl-find_object.c
|
||||
+++ b/elf/dl-find_object.c
|
||||
@@ -468,6 +468,37 @@ rtld_hidden_def (__dl_find_object_internal)
|
||||
strong_alias (__dl_find_object_internal, _dl_find_object)
|
||||
#endif
|
||||
|
||||
+/* 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;
|
||||
+ ElfW(Addr) start = ph->p_vaddr + map->l_addr;
|
||||
+ _dlfo_nodelete_mappings[nodelete].map_start = start;
|
||||
+ _dlfo_nodelete_mappings[nodelete].map_end = 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
|
||||
@@ -479,29 +510,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)
|
||||
@@ -513,11 +523,22 @@ _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;
|
||||
+ /* The kernel may have loaded ld.so with gaps. */
|
||||
+ if (!l->l_contiguous
|
||||
+#ifdef SHARED
|
||||
+ && l == &GL(dl_rtld_map)
|
||||
+#endif
|
||||
+ )
|
||||
+ nodelete
|
||||
+ = _dlfo_process_initial_noncontiguous_map (l, nodelete);
|
||||
+ else
|
||||
+ {
|
||||
+ 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)
|
||||
{
|
||||
@@ -767,7 +788,6 @@ _dl_find_object_update_1 (struct link_map **loaded, size_t count)
|
||||
/* Prefer newly loaded link map. */
|
||||
assert (loaded_index1 > 0);
|
||||
_dl_find_object_from_map (loaded[loaded_index1 - 1], dlfo);
|
||||
- loaded[loaded_index1 - 1]->l_find_object_processed = 1;
|
||||
--loaded_index1;
|
||||
}
|
||||
|
||||
diff --git a/elf/dl-find_object.h b/elf/dl-find_object.h
|
||||
index 11569efc9b7daf9c..fae25747edc6dca0 100644
|
||||
--- a/elf/dl-find_object.h
|
||||
+++ b/elf/dl-find_object.h
|
||||
@@ -87,7 +87,7 @@ _dl_find_object_to_external (struct dl_find_object_internal *internal,
|
||||
}
|
||||
|
||||
/* Extract the object location data from a link map and writes it to
|
||||
- *RESULT using relaxed MO stores. */
|
||||
+ *RESULT using relaxed MO stores. Set L->l_find_object_processed. */
|
||||
static void __attribute__ ((unused))
|
||||
_dl_find_object_from_map (struct link_map *l,
|
||||
struct dl_find_object_internal *result)
|
||||
@@ -100,6 +100,8 @@ _dl_find_object_from_map (struct link_map *l,
|
||||
atomic_store_relaxed (&result->eh_dbase, (void *) l->l_info[DT_PLTGOT]);
|
||||
#endif
|
||||
|
||||
+ l->l_find_object_processed = 1;
|
||||
+
|
||||
for (const ElfW(Phdr) *ph = l->l_phdr, *ph_end = l->l_phdr + l->l_phnum;
|
||||
ph < ph_end; ++ph)
|
||||
if (ph->p_type == DLFO_EH_SEGMENT_TYPE)
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index a9073d4e14b07410..425003e6c8e452ab 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -1308,7 +1308,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)
|
||||
{
|
||||
@@ -1334,6 +1334,29 @@ rtld_setup_phdr (void)
|
||||
GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum;
|
||||
|
||||
|
||||
+ GL(dl_rtld_map).l_contiguous = 1;
|
||||
+ /* 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 (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)
|
||||
diff --git a/elf/tst-link-map-contiguous-ldso.c b/elf/tst-link-map-contiguous-ldso.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..04de808bb234fe38
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-link-map-contiguous-ldso.c
|
||||
@@ -0,0 +1,98 @@
|
||||
+/* Check that _dl_find_object behavior matches up with gaps.
|
||||
+ Copyright (C) 2025 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
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <dlfcn.h>
|
||||
+#include <gnu/lib-names.h>
|
||||
+#include <link.h>
|
||||
+#include <stdbool.h>
|
||||
+#include <stdio.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+#include <support/xunistd.h>
|
||||
+#include <sys/mman.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ struct link_map *l = xdlopen (LD_SO, RTLD_NOW);
|
||||
+ if (!l->l_contiguous)
|
||||
+ {
|
||||
+ puts ("info: ld.so link map is not contiguous");
|
||||
+
|
||||
+ /* Try to find holes by probing with mmap. */
|
||||
+ int pagesize = getpagesize ();
|
||||
+ bool gap_found = false;
|
||||
+ ElfW(Addr) addr = l->l_map_start;
|
||||
+ TEST_COMPARE (addr % pagesize, 0);
|
||||
+ while (addr < l->l_map_end)
|
||||
+ {
|
||||
+ void *expected = (void *) addr;
|
||||
+ void *ptr = xmmap (expected, 1, PROT_READ | PROT_WRITE,
|
||||
+ MAP_PRIVATE | MAP_ANONYMOUS, -1);
|
||||
+ struct dl_find_object dlfo;
|
||||
+ int dlfo_ret = _dl_find_object (expected, &dlfo);
|
||||
+ if (ptr == expected)
|
||||
+ {
|
||||
+ if (dlfo_ret < 0)
|
||||
+ {
|
||||
+ TEST_COMPARE (dlfo_ret, -1);
|
||||
+ printf ("info: hole without mapping data found at %p\n", ptr);
|
||||
+ }
|
||||
+ else
|
||||
+ FAIL ("object \"%s\" found in gap at %p",
|
||||
+ dlfo.dlfo_link_map->l_name, ptr);
|
||||
+ gap_found = true;
|
||||
+ }
|
||||
+ else if (dlfo_ret == 0)
|
||||
+ {
|
||||
+ if ((void *) dlfo.dlfo_link_map != (void *) l)
|
||||
+ {
|
||||
+ printf ("info: object \"%s\" found at %p\n",
|
||||
+ dlfo.dlfo_link_map->l_name, ptr);
|
||||
+ gap_found = true;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ TEST_COMPARE (dlfo_ret, -1);
|
||||
+ xmunmap (ptr, 1);
|
||||
+ addr += pagesize;
|
||||
+ }
|
||||
+ if (!gap_found)
|
||||
+ FAIL ("no ld.so gap found");
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ puts ("info: ld.so link map is contiguous");
|
||||
+
|
||||
+ /* Assert that ld.so is truly contiguous in memory. */
|
||||
+ volatile long int *p = (volatile long int *) l->l_map_start;
|
||||
+ volatile long int *end = (volatile long int *) l->l_map_end;
|
||||
+ while (p < end)
|
||||
+ {
|
||||
+ *p;
|
||||
+ ++p;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ xdlclose (l);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
diff --git a/elf/tst-link-map-contiguous-libc.c b/elf/tst-link-map-contiguous-libc.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..eb5728c765ac3cfb
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-link-map-contiguous-libc.c
|
||||
@@ -0,0 +1,57 @@
|
||||
+/* Check that the entire libc.so program image is readable if contiguous.
|
||||
+ Copyright (C) 2025 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
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <gnu/lib-names.h>
|
||||
+#include <link.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+#include <support/xunistd.h>
|
||||
+#include <sys/mman.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ struct link_map *l = xdlopen (LIBC_SO, RTLD_NOW);
|
||||
+
|
||||
+ /* The dynamic loader fills holes with PROT_NONE mappings. */
|
||||
+ if (!l->l_contiguous)
|
||||
+ FAIL_EXIT1 ("libc.so link map is not contiguous");
|
||||
+
|
||||
+ /* Direct probing does not work because not everything is readable
|
||||
+ due to PROT_NONE mappings. */
|
||||
+ int pagesize = getpagesize ();
|
||||
+ ElfW(Addr) addr = l->l_map_start;
|
||||
+ TEST_COMPARE (addr % pagesize, 0);
|
||||
+ while (addr < l->l_map_end)
|
||||
+ {
|
||||
+ void *expected = (void *) addr;
|
||||
+ void *ptr = xmmap (expected, 1, PROT_READ | PROT_WRITE,
|
||||
+ MAP_PRIVATE | MAP_ANONYMOUS, -1);
|
||||
+ if (ptr == expected)
|
||||
+ FAIL ("hole in libc.so memory image after %lu bytes",
|
||||
+ (unsigned long int) (addr - l->l_map_start));
|
||||
+ xmunmap (ptr, 1);
|
||||
+ addr += pagesize;
|
||||
+ }
|
||||
+
|
||||
+ xdlclose (l);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+#include <support/test-driver.c>
|
||||
diff --git a/elf/tst-link-map-contiguous-main.c b/elf/tst-link-map-contiguous-main.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..2d1a054f0fbb0855
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-link-map-contiguous-main.c
|
||||
@@ -0,0 +1,45 @@
|
||||
+/* Check that the entire main program image is readable if contiguous.
|
||||
+ Copyright (C) 2025 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
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <link.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ struct link_map *l = xdlopen ("", RTLD_NOW);
|
||||
+ if (!l->l_contiguous)
|
||||
+ FAIL_UNSUPPORTED ("main link map is not contiguous");
|
||||
+
|
||||
+ /* This check only works if the kernel loaded the main program. The
|
||||
+ dynamic loader replaces gaps with PROT_NONE mappings, resulting
|
||||
+ in faults. */
|
||||
+ volatile long int *p = (volatile long int *) l->l_map_start;
|
||||
+ volatile long int *end = (volatile long int *) l->l_map_end;
|
||||
+ while (p < end)
|
||||
+ {
|
||||
+ *p;
|
||||
+ ++p;
|
||||
+ }
|
||||
+
|
||||
+ xdlclose (l);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+#include <support/test-driver.c>
|
||||
@ -157,7 +157,7 @@ end \
|
||||
Summary: The GNU libc libraries
|
||||
Name: glibc
|
||||
Version: %{glibcversion}
|
||||
Release: 227%{?dist}
|
||||
Release: 228%{?dist}
|
||||
|
||||
# In general, GPLv2+ is used by programs, LGPLv2+ is used for
|
||||
# libraries.
|
||||
@ -1336,6 +1336,8 @@ Patch1020: glibc-RHEL-50086-2.patch
|
||||
Patch1021: glibc-RHEL-107564.patch
|
||||
Patch1022: glibc-RHEL-105965.patch
|
||||
Patch1023: glibc-RHEL-108221.patch
|
||||
Patch1024: glibc-RHEL-104852-1.patch
|
||||
Patch1025: glibc-RHEL-104852-2.patch
|
||||
|
||||
##############################################################################
|
||||
# Continued list of core "glibc" package information:
|
||||
@ -3423,6 +3425,9 @@ update_gconv_modules_cache ()
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Mon Aug 11 2025 Florian Weimer <fweimer@redhat.com> - 2.34-228
|
||||
- Handle load segment gaps in _dl_find_object (RHEL-104852)
|
||||
|
||||
* Mon Aug 11 2025 Florian Weimer <fweimer@redhat.com> - 2.34-227
|
||||
- Disable failing subtest of elf/tst-dl_find_object-static (RHEL-108221)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user