diff --git a/glibc-RHEL-47403-1.patch b/glibc-RHEL-47403-1.patch new file mode 100644 index 0000000..96d4c50 --- /dev/null +++ b/glibc-RHEL-47403-1.patch @@ -0,0 +1,562 @@ +commit a93d9e03a31ec14405cb3a09aa95413b67067380 +Author: H.J. Lu +Date: Tue Aug 17 19:35:48 2021 -0700 + + Extend struct r_debug to support multiple namespaces [BZ #15971] + + Glibc does not provide an interface for debugger to access libraries + loaded in multiple namespaces via dlmopen. + + The current rtld-debugger interface is described in the file: + + elf/rtld-debugger-interface.txt + + under the "Standard debugger interface" heading. This interface only + provides access to the first link-map (LM_ID_BASE). + + 1. Bump r_version to 2 when multiple namespaces are used. This triggers + the GDB bug: + + https://sourceware.org/bugzilla/show_bug.cgi?id=28236 + + 2. Add struct r_debug_extended to extend struct r_debug into a linked-list, + where each element correlates to an unique namespace. + 3. Initialize the r_debug_extended structure. Bump r_version to 2 for + the new namespace and add the new namespace to the namespace linked list. + 4. Add _dl_debug_update to return the address of struct r_debug' of a + namespace. + 5. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended. + 6. Provide the symbol, _r_debug, with size of struct r_debug, as an alias + of _r_debug_extended, for programs which reference _r_debug. + + This fixes BZ #15971. + + Reviewed-by: Florian Weimer + +Conflicts: + elf/Makefile (fixup context due to reordering) + elf/dl-open.c (modified line didn't exist downstream) + +diff --git a/csu/Makefile b/csu/Makefile +index 3054329cea4a276d..e2390e4a7deb6941 100644 +--- a/csu/Makefile ++++ b/csu/Makefile +@@ -88,6 +88,9 @@ endif + before-compile += $(objpfx)abi-tag.h + generated += abi-tag.h + ++# Put it here to generate it earlier. ++gen-as-const-headers += rtld-sizes.sym ++ + # These are the special initializer/finalizer files. They are always the + # first and last file in the link. crti.o ... crtn.o define the global + # "functions" _init and _fini to run the .init and .fini sections. +diff --git a/csu/rtld-sizes.sym b/csu/rtld-sizes.sym +new file mode 100644 +index 0000000000000000..13924d5efdbaa248 +--- /dev/null ++++ b/csu/rtld-sizes.sym +@@ -0,0 +1,6 @@ ++#include ++ ++-- ++R_DEBUG_SIZE sizeof (struct r_debug) ++R_DEBUG_EXTENDED_SIZE sizeof (struct r_debug_extended) ++R_DEBUG_EXTENDED_ALIGN __alignof (struct r_debug_extended) +diff --git a/elf/Makefile b/elf/Makefile +index 15bec14364266c77..b074cc29664b3e20 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -54,6 +54,7 @@ dl-routines = \ + dl-catch \ + dl-close \ + dl-debug \ ++ dl-debug-symbols \ + dl-deps \ + dl-exception \ + dl-execstack \ +@@ -399,6 +400,7 @@ tests += \ + tst-dlmodcount \ + tst-dlmopen1 \ + tst-dlmopen3 \ ++ tst-dlmopen4 \ + tst-dlmopen-dlerror \ + tst-dlmopen-gethostbyname \ + tst-dlmopen-twice \ +@@ -1973,6 +1975,8 @@ $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so + + $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so + ++$(objpfx)tst-dlmopen4.out: $(objpfx)tst-dlmopen1mod.so ++ + $(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so + tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + +diff --git a/elf/dl-close.c b/elf/dl-close.c +index 9d158c25498fd8ae..bf699e58d753a1e2 100644 +--- a/elf/dl-close.c ++++ b/elf/dl-close.c +@@ -469,7 +469,7 @@ _dl_close_worker (struct link_map *map, bool force) + #endif + + /* Notify the debugger we are about to remove some loaded objects. */ +- struct r_debug *r = _dl_debug_initialize (0, nsid); ++ struct r_debug *r = _dl_debug_update (nsid); + r->r_state = RT_DELETE; + _dl_debug_state (); + LIBC_PROBE (unmap_start, 2, nsid, r); +diff --git a/elf/dl-debug-symbols.S b/elf/dl-debug-symbols.S +new file mode 100644 +index 0000000000000000..b7e9f5d9470c4da2 +--- /dev/null ++++ b/elf/dl-debug-symbols.S +@@ -0,0 +1,36 @@ ++/* Define symbols used to communicate dynamic linker state to the ++ debugger at runtime. ++ 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 ++ . */ ++ ++#include ++ ++/* Define 2 symbols, _r_debug_extended and _r_debug, which is an alias ++ of _r_debug_extended, but with the size of struct r_debug. */ ++ ++ .globl _r_debug ++ .type _r_debug, %object ++ .size _r_debug, R_DEBUG_SIZE ++ .hidden _r_debug_extended ++ .globl _r_debug_extended ++ .type _r_debug_extended, %object ++ .size _r_debug_extended, R_DEBUG_EXTENDED_SIZE ++ .section .bss ++ .balign R_DEBUG_EXTENDED_ALIGN ++_r_debug: ++_r_debug_extended: ++ .zero R_DEBUG_EXTENDED_SIZE +diff --git a/elf/dl-debug.c b/elf/dl-debug.c +index 2cd5f0975380445c..f637d4bb8de3db8c 100644 +--- a/elf/dl-debug.c ++++ b/elf/dl-debug.c +@@ -30,37 +30,80 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr) + && VERIFY_MEMBER (l_prev)) + ? 1 : -1]; + +-/* This structure communicates dl state to the debugger. The debugger +- normally finds it via the DT_DEBUG entry in the dynamic section, but in +- a statically-linked program there is no dynamic section for the debugger +- to examine and it looks for this particular symbol name. */ +-struct r_debug _r_debug; ++/* Update the `r_map' member and return the address of `struct r_debug' ++ of the namespace NS. */ + ++struct r_debug * ++_dl_debug_update (Lmid_t ns) ++{ ++ struct r_debug_extended *r; ++ if (ns == LM_ID_BASE) ++ r = &_r_debug_extended; ++ else ++ r = &GL(dl_ns)[ns]._ns_debug; ++ if (r->base.r_map == NULL) ++ atomic_store_release (&r->base.r_map, ++ (void *) GL(dl_ns)[ns]._ns_loaded); ++ return &r->base; ++} + +-/* Initialize _r_debug if it has not already been done. The argument is +- the run-time load address of the dynamic linker, to be put in +- _r_debug.r_ldbase. Returns the address of _r_debug. */ ++/* Initialize _r_debug_extended for the namespace NS. LDBASE is the ++ run-time load address of the dynamic linker, to be put in ++ _r_debug_extended.r_ldbase. Return the address of _r_debug. */ + + struct r_debug * + _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + { +- struct r_debug *r; ++ struct r_debug_extended *r, **pp = NULL; + + if (ns == LM_ID_BASE) +- r = &_r_debug; +- else +- r = &GL(dl_ns)[ns]._ns_debug; ++ { ++ r = &_r_debug_extended; ++ /* Initialize r_version to 1. */ ++ if (_r_debug_extended.base.r_version == 0) ++ _r_debug_extended.base.r_version = 1; ++ } ++ else if (DL_NNS > 1) ++ { ++ r = &GL(dl_ns)[ns]._ns_debug; ++ if (r->base.r_brk == 0) ++ { ++ /* Add the new namespace to the linked list. After a namespace ++ is initialized, r_brk becomes non-zero. A namespace becomes ++ empty (r_map == NULL) when it is unused. But it is never ++ removed from the linked list. */ ++ struct r_debug_extended *p; ++ for (pp = &_r_debug_extended.r_next; ++ (p = *pp) != NULL; ++ pp = &p->r_next) ++ ; ++ ++ r->base.r_version = 2; ++ } ++ } ++ ++ if (r->base.r_brk == 0) ++ { ++ /* Tell the debugger where to find the map of loaded objects. ++ This function is called from dlopen. Initialize the namespace ++ only once. */ ++ r->base.r_ldbase = ldbase ?: _r_debug_extended.base.r_ldbase; ++ r->base.r_brk = (ElfW(Addr)) &_dl_debug_state; ++ r->r_next = NULL; ++ } ++ ++ if (r->base.r_map == NULL) ++ atomic_store_release (&r->base.r_map, ++ (void *) GL(dl_ns)[ns]._ns_loaded); + +- if (r->r_map == NULL || ldbase != 0) ++ if (pp != NULL) + { +- /* Tell the debugger where to find the map of loaded objects. */ +- r->r_version = 1 /* R_DEBUG_VERSION XXX */; +- r->r_ldbase = ldbase ?: _r_debug.r_ldbase; +- r->r_map = (void *) GL(dl_ns)[ns]._ns_loaded; +- r->r_brk = (ElfW(Addr)) &_dl_debug_state; ++ atomic_store_release (pp, r); ++ /* Bump r_version to 2 for the new namespace. */ ++ atomic_store_release (&_r_debug_extended.base.r_version, 2); + } + +- return r; ++ return &r->base; + } + + +diff --git a/elf/dl-load.c b/elf/dl-load.c +index eb6b658b698f5694..5b0734c816b351f0 100644 +--- a/elf/dl-load.c ++++ b/elf/dl-load.c +@@ -950,7 +950,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, + /* Initialize to keep the compiler happy. */ + const char *errstring = NULL; + int errval = 0; +- struct r_debug *r = _dl_debug_initialize (0, nsid); ++ struct r_debug *r = _dl_debug_update (nsid); + bool make_consistent = false; + + /* Get file information. To match the kernel behavior, do not fill +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 80af6518d4395906..eef724f7e9b2211d 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -596,7 +596,7 @@ dl_open_worker_begin (void *a) + if ((mode & RTLD_GLOBAL) && new->l_global == 0) + add_to_global_update (new); + +- assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); ++ assert (_dl_debug_update (args->nsid)->r_state == RT_CONSISTENT); + + return; + } +@@ -634,7 +634,7 @@ dl_open_worker_begin (void *a) + #endif + + /* Notify the debugger all new objects are now ready to go. */ +- struct r_debug *r = _dl_debug_initialize (0, args->nsid); ++ struct r_debug *r = _dl_debug_update (args->nsid); + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + LIBC_PROBE (map_complete, 3, args->nsid, r, new); +@@ -840,7 +840,7 @@ no more namespaces available for dlmopen()")); + } + + GL(dl_ns)[nsid].libc_map = NULL; +- _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT; ++ _dl_debug_update (nsid)->r_state = RT_CONSISTENT; + } + /* Never allow loading a DSO in a namespace which is empty. Such + direct placements is only causing problems. Also don't allow +@@ -916,7 +916,7 @@ no more namespaces available for dlmopen()")); + _dl_signal_exception (errcode, &exception, NULL); + } + +- assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT); ++ assert (_dl_debug_update (args.nsid)->r_state == RT_CONSISTENT); + + /* Release the lock. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); +diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c +index 757205affe65d9e1..5b85df8a2eed1888 100644 +--- a/elf/dl-reloc-static-pie.c ++++ b/elf/dl-reloc-static-pie.c +@@ -62,7 +62,7 @@ _dl_relocate_static_pie (void) + ELF_DYNAMIC_RELOCATE (main_map, NULL, 0, 0, 0); + main_map->l_relocated = 1; + +- /* Initialize _r_debug. */ ++ /* Initialize _r_debug_extended. */ + struct r_debug *r = _dl_debug_initialize (0, LM_ID_BASE); + r->r_state = RT_CONSISTENT; + +diff --git a/elf/link.h b/elf/link.h +index 21a351686b9bf7c8..0906bbe33fbd9f8f 100644 +--- a/elf/link.h ++++ b/elf/link.h +@@ -34,14 +34,13 @@ + #include /* Defines __ELF_NATIVE_CLASS. */ + #include + +-/* Rendezvous structure used by the run-time dynamic linker to communicate +- details of shared object loading to the debugger. If the executable's +- dynamic section has a DT_DEBUG element, the run-time linker sets that +- element's value to the address where this structure can be found. */ ++/* The legacy rendezvous structure used by the run-time dynamic linker to ++ communicate details of shared object loading to the debugger. */ + + struct r_debug + { +- int r_version; /* Version number for this protocol. */ ++ /* Version number for this protocol. It should be greater than 0. */ ++ int r_version; + + struct link_map *r_map; /* Head of the chain of loaded objects. */ + +@@ -63,16 +62,34 @@ struct r_debug + ElfW(Addr) r_ldbase; /* Base address the linker is loaded at. */ + }; + +-/* This is the instance of that structure used by the dynamic linker. */ ++/* This is the symbol of that structure provided by the dynamic linker. */ + extern struct r_debug _r_debug; + ++/* The extended rendezvous structure used by the run-time dynamic linker ++ to communicate details of shared object loading to the debugger. If ++ the executable's dynamic section has a DT_DEBUG element, the run-time ++ linker sets that element's value to the address where this structure ++ can be found. */ ++ ++struct r_debug_extended ++ { ++ struct r_debug base; ++ ++ /* The following field is added by r_version == 2. */ ++ ++ /* Link to the next r_debug_extended structure. Each r_debug_extended ++ structure represents a different namespace. The first ++ r_debug_extended structure is for the default namespace. */ ++ struct r_debug_extended *r_next; ++ }; ++ + /* This symbol refers to the "dynamic structure" in the `.dynamic' section + of whatever module refers to `_DYNAMIC'. So, to find its own +- `struct r_debug', a program could do: ++ `struct r_debug_extended', a program could do: + for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn) + if (dyn->d_tag == DT_DEBUG) +- r_debug = (struct r_debug *) dyn->d_un.d_ptr; +- */ ++ r_debug_extended = (struct r_debug_extended *) dyn->d_un.d_ptr; ++ */ + extern ElfW(Dyn) _DYNAMIC[]; + + /* Structure describing a loaded shared object. The `l_next' and `l_prev' +diff --git a/elf/rtld-debugger-interface.txt b/elf/rtld-debugger-interface.txt +index 61bc99e4b086612f..f3476d8308616f84 100644 +--- a/elf/rtld-debugger-interface.txt ++++ b/elf/rtld-debugger-interface.txt +@@ -9,6 +9,9 @@ structure can be found. + + The r_debug structure contains (amongst others) the following fields: + ++ int r_version: ++ Version number for this protocol. It should be greater than 0. ++ + struct link_map *r_map: + A linked list of loaded objects. + +@@ -32,6 +35,18 @@ but there is no way for the debugger to discover whether any of the + objects in the link-map have been relocated or not. + + ++Extension to the r_debug structure ++================================== ++ ++The r_debug_extended structure is an extension of the r_debug interface. ++If r_version is 2, one additional field is available: ++ ++ struct r_debug_extended *r_next; ++ Link to the next r_debug_extended structure. Each r_debug_extended ++ structure represents a different namespace. A namespace is active ++ if its r_map field isn't NULL. The first r_debug_extended structure ++ is for the default namespace. ++ + Probe-based debugger interface + ============================== + +diff --git a/elf/rtld.c b/elf/rtld.c +index d3d9e6b904ac78fd..7d8ed0ac1188d527 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -1725,7 +1725,7 @@ dl_main (const ElfW(Phdr) *phdr, + objects. */ + call_init_paths (&state); + +- /* Initialize _r_debug. */ ++ /* Initialize _r_debug_extended. */ + struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr, + LM_ID_BASE); + r->r_state = RT_CONSISTENT; +@@ -2531,7 +2531,7 @@ dl_main (const ElfW(Phdr) *phdr, + + /* Notify the debugger all new objects are now ready to go. We must re-get + the address since by now the variable might be in another object. */ +- r = _dl_debug_initialize (0, LM_ID_BASE); ++ r = _dl_debug_update (LM_ID_BASE); + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + LIBC_PROBE (init_complete, 2, LM_ID_BASE, r); +diff --git a/elf/tst-dlmopen4.c b/elf/tst-dlmopen4.c +new file mode 100644 +index 0000000000000000..3fe150e50bc259f0 +--- /dev/null ++++ b/elf/tst-dlmopen4.c +@@ -0,0 +1,72 @@ ++/* Test struct r_debug_extended via DT_DEBUG. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef ELF_MACHINE_GET_R_DEBUG ++# define ELF_MACHINE_GET_R_DEBUG(d) \ ++ (__extension__ ({ \ ++ struct r_debug_extended *debug; \ ++ if ((d)->d_tag == DT_DEBUG) \ ++ debug = (struct r_debug_extended *) (d)->d_un.d_ptr; \ ++ else \ ++ debug = NULL; \ ++ debug; })) ++#endif ++ ++static int ++do_test (void) ++{ ++ ElfW(Dyn) *d; ++ struct r_debug_extended *debug = NULL; ++ ++ for (d = _DYNAMIC; d->d_tag != DT_NULL; ++d) ++ { ++ debug = ELF_MACHINE_GET_R_DEBUG (d); ++ if (debug != NULL) ++ break; ++ } ++ ++ TEST_VERIFY_EXIT (debug != NULL); ++ TEST_COMPARE (debug->base.r_version, 1); ++ TEST_VERIFY_EXIT (debug->r_next == NULL); ++ ++ void *h = xdlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so", ++ RTLD_LAZY); ++ ++ TEST_COMPARE (debug->base.r_version, 2); ++ TEST_VERIFY_EXIT (debug->r_next != NULL); ++ TEST_VERIFY_EXIT (debug->r_next->r_next == NULL); ++ TEST_VERIFY_EXIT (debug->r_next->base.r_map != NULL); ++ TEST_VERIFY_EXIT (debug->r_next->base.r_map->l_name != NULL); ++ const char *name = basename (debug->r_next->base.r_map->l_name); ++ TEST_COMPARE_STRING (name, "tst-dlmopen1mod.so"); ++ ++ xdlclose (h); ++ ++ return 0; ++} ++ ++#include +diff --git a/include/link.h b/include/link.h +index 0dc63ef37d6a5666..0cf130ddb8af2e89 100644 +--- a/include/link.h ++++ b/include/link.h +@@ -362,6 +362,10 @@ struct auditstate + }; + + ++/* This is the hidden instance of struct r_debug_extended used by the ++ dynamic linker. */ ++extern struct r_debug_extended _r_debug_extended attribute_hidden; ++ + #if __ELF_NATIVE_CLASS == 32 + # define symbind symbind32 + # define LA_SYMBIND "la_symbind32" +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index dc4e0555e4ed7f3c..695f3910234e87da 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -376,7 +376,7 @@ struct rtld_global + void (*free) (void *); + } _ns_unique_sym_table; + /* Keep track of changes to each namespace' list. */ +- struct r_debug _ns_debug; ++ struct r_debug_extended _ns_debug; + } _dl_ns[DL_NNS]; + /* One higher than index of last used namespace. */ + EXTERN size_t _dl_nns; +@@ -1128,12 +1128,16 @@ extern void _dl_sort_maps (struct link_map **maps, unsigned int nmaps, + extern void _dl_debug_state (void); + rtld_hidden_proto (_dl_debug_state) + +-/* Initialize `struct r_debug' if it has not already been done. The +- argument is the run-time load address of the dynamic linker, to be put +- in the `r_ldbase' member. Returns the address of the structure. */ ++/* Initialize `struct r_debug_extended' for the namespace NS. LDBASE ++ is the run-time load address of the dynamic linker, to be put in the ++ `r_ldbase' member. Return the address of the structure. */ + extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + attribute_hidden; + ++/* Update the `r_map' member and return the address of `struct r_debug' ++ of the namespace NS. */ ++extern struct r_debug *_dl_debug_update (Lmid_t ns) attribute_hidden; ++ + /* Initialize the basic data structure for the search paths. SOURCE + is either "LD_LIBRARY_PATH" or "--library-path". + GLIBC_HWCAPS_PREPEND adds additional glibc-hwcaps subdirectories to diff --git a/glibc-RHEL-47403-10.patch b/glibc-RHEL-47403-10.patch new file mode 100644 index 0000000..bc3a0d6 --- /dev/null +++ b/glibc-RHEL-47403-10.patch @@ -0,0 +1,158 @@ +commit 7278d11f3a0cd528188c719bab75575b0aea2c6e +Author: Florian Weimer +Date: Fri Jul 4 21:46:05 2025 +0200 + + elf: Introduce separate _r_debug_array variable + + It replaces the ns_debug member of the namespaces. Previously, + the base namespace had an unused ns_debug member. + + This change also fixes a concurrency issue: Now _dl_debug_initialize + only updates r_next of the previous namespace's r_debug after the new + r_debug is initialized, so that only the initialized version is + observed. (Client code accessing _r_debug will benefit from load + dependency tracking in CPUs even without explicit barriers.) + + Reviewed-by: H.J. Lu + +diff --git a/elf/dl-debug.c b/elf/dl-debug.c +index f637d4bb8de3db8c..649386d5a6b885ed 100644 +--- a/elf/dl-debug.c ++++ b/elf/dl-debug.c +@@ -30,17 +30,37 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr) + && VERIFY_MEMBER (l_prev)) + ? 1 : -1]; + ++#ifdef SHARED ++/* r_debug structs for secondary namespaces. The first namespace is ++ handled separately because its r_debug structure must overlap with ++ the public _r_debug symbol, so the first array element corresponds ++ to LM_ID_BASE + 1. See elf/dl-debug-symbols.S. */ ++struct r_debug_extended _r_debug_array[DL_NNS - 1]; ++ ++/* Return the r_debug object for the namespace NS. */ ++static inline struct r_debug_extended * ++get_rdebug (Lmid_t ns) ++{ ++ if (ns == LM_ID_BASE) ++ return &_r_debug_extended; ++ else ++ return &_r_debug_array[ns - 1]; ++} ++#else /* !SHARED */ ++static inline struct r_debug_extended * ++get_rdebug (Lmid_t ns) ++{ ++ return &_r_debug_extended; /* There is just one namespace. */ ++} ++#endif /* !SHARED */ ++ + /* Update the `r_map' member and return the address of `struct r_debug' + of the namespace NS. */ + + struct r_debug * + _dl_debug_update (Lmid_t ns) + { +- struct r_debug_extended *r; +- if (ns == LM_ID_BASE) +- r = &_r_debug_extended; +- else +- r = &GL(dl_ns)[ns]._ns_debug; ++ struct r_debug_extended *r = get_rdebug (ns); + if (r->base.r_map == NULL) + atomic_store_release (&r->base.r_map, + (void *) GL(dl_ns)[ns]._ns_loaded); +@@ -54,34 +74,7 @@ _dl_debug_update (Lmid_t ns) + struct r_debug * + _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + { +- struct r_debug_extended *r, **pp = NULL; +- +- if (ns == LM_ID_BASE) +- { +- r = &_r_debug_extended; +- /* Initialize r_version to 1. */ +- if (_r_debug_extended.base.r_version == 0) +- _r_debug_extended.base.r_version = 1; +- } +- else if (DL_NNS > 1) +- { +- r = &GL(dl_ns)[ns]._ns_debug; +- if (r->base.r_brk == 0) +- { +- /* Add the new namespace to the linked list. After a namespace +- is initialized, r_brk becomes non-zero. A namespace becomes +- empty (r_map == NULL) when it is unused. But it is never +- removed from the linked list. */ +- struct r_debug_extended *p; +- for (pp = &_r_debug_extended.r_next; +- (p = *pp) != NULL; +- pp = &p->r_next) +- ; +- +- r->base.r_version = 2; +- } +- } +- ++ struct r_debug_extended *r = get_rdebug (ns); + if (r->base.r_brk == 0) + { + /* Tell the debugger where to find the map of loaded objects. +@@ -89,20 +82,36 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) + only once. */ + r->base.r_ldbase = ldbase ?: _r_debug_extended.base.r_ldbase; + r->base.r_brk = (ElfW(Addr)) &_dl_debug_state; +- r->r_next = NULL; ++ ++#ifdef SHARED ++ /* Add the new namespace to the linked list. This assumes that ++ namespaces are allocated in increasing order. After a ++ namespace is initialized, r_brk becomes non-zero. A ++ namespace becomes empty (r_map == NULL) when it is unused. ++ But it is never removed from the linked list. */ ++ ++ if (ns != LM_ID_BASE) ++ { ++ r->base.r_version = 2; ++ if (ns - 1 == LM_ID_BASE) ++ { ++ atomic_store_release (&_r_debug_extended.r_next, r); ++ /* Now there are multiple namespaces. */ ++ atomic_store_release (&_r_debug_extended.base.r_version, 2); ++ } ++ else ++ /* Update r_debug_extended of the previous namespace. */ ++ atomic_store_release (&_r_debug_array[ns - 2].r_next, r); ++ } ++ else ++#endif /* SHARED */ ++ r->base.r_version = 1; + } + + if (r->base.r_map == NULL) + atomic_store_release (&r->base.r_map, + (void *) GL(dl_ns)[ns]._ns_loaded); + +- if (pp != NULL) +- { +- atomic_store_release (pp, r); +- /* Bump r_version to 2 for the new namespace. */ +- atomic_store_release (&_r_debug_extended.base.r_version, 2); +- } +- + return &r->base; + } + +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index 695f3910234e87da..e09edb01da3b5b90 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -375,8 +375,6 @@ struct rtld_global + size_t n_elements; + void (*free) (void *); + } _ns_unique_sym_table; +- /* Keep track of changes to each namespace' list. */ +- struct r_debug_extended _ns_debug; + } _dl_ns[DL_NNS]; + /* One higher than index of last used namespace. */ + EXTERN size_t _dl_nns; diff --git a/glibc-RHEL-47403-11.patch b/glibc-RHEL-47403-11.patch new file mode 100644 index 0000000..c95eba8 --- /dev/null +++ b/glibc-RHEL-47403-11.patch @@ -0,0 +1,17 @@ +Downstream patch to keep ABI stable. +Bring back a dummy `struct r_debug` member in `GL (dl_ns)`, to preserve +`_rtld_global` layout. + +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index e09edb01da3b5b90..3d9b90a22bfa6a7d 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -375,6 +375,8 @@ struct rtld_global + size_t n_elements; + void (*free) (void *); + } _ns_unique_sym_table; ++ /* Dummy structure to keep the ABI stable. */ ++ struct r_debug _ns_debug_unused; + } _dl_ns[DL_NNS]; + /* One higher than index of last used namespace. */ + EXTERN size_t _dl_nns; diff --git a/glibc-RHEL-47403-2.patch b/glibc-RHEL-47403-2.patch new file mode 100644 index 0000000..d7861e9 --- /dev/null +++ b/glibc-RHEL-47403-2.patch @@ -0,0 +1,26 @@ +commit 7e84ac3a3ac9e7c4dc10de2ce65db971b9650e4d +Author: Florian Weimer +Date: Mon Sep 20 15:50:00 2021 +0200 + + elf: Include in elf/dl-debug-symbols.S + + This is necessary to generate assembler marker sections on some + targets. + + Reviewed-by: Szabolcs Nagy + +diff --git a/elf/dl-debug-symbols.S b/elf/dl-debug-symbols.S +index b7e9f5d9470c4da2..28456ab1f237ea87 100644 +--- a/elf/dl-debug-symbols.S ++++ b/elf/dl-debug-symbols.S +@@ -18,6 +18,10 @@ + . */ + + #include ++#include ++ ++/* Some targets define a macro to denote the zero register. */ ++#undef zero + + /* Define 2 symbols, _r_debug_extended and _r_debug, which is an alias + of _r_debug_extended, but with the size of struct r_debug. */ diff --git a/glibc-RHEL-47403-3.patch b/glibc-RHEL-47403-3.patch new file mode 100644 index 0000000..58de62a --- /dev/null +++ b/glibc-RHEL-47403-3.patch @@ -0,0 +1,32 @@ +commit 1b5e65ef6a442fdccf88d43c3048f98292d85631 +Author: Paul Pluzhnikov +Date: Sat Mar 25 21:27:01 2023 +0000 + + Minor: don't call _dl_debug_update (which can have side effects) inside assert + +diff --git a/elf/dl-open.c b/elf/dl-open.c +index eef724f7e9b2211d..0d2b4cd4785a226a 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -596,7 +596,9 @@ dl_open_worker_begin (void *a) + if ((mode & RTLD_GLOBAL) && new->l_global == 0) + add_to_global_update (new); + +- assert (_dl_debug_update (args->nsid)->r_state == RT_CONSISTENT); ++ const int r_state __attribute__ ((unused)) ++ = _dl_debug_update (args->nsid)->r_state; ++ assert (r_state == RT_CONSISTENT); + + return; + } +@@ -916,7 +918,9 @@ no more namespaces available for dlmopen()")); + _dl_signal_exception (errcode, &exception, NULL); + } + +- assert (_dl_debug_update (args.nsid)->r_state == RT_CONSISTENT); ++ const int r_state __attribute__ ((unused)) ++ = _dl_debug_update (args.nsid)->r_state; ++ assert (r_state == RT_CONSISTENT); + + /* Release the lock. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); diff --git a/glibc-RHEL-47403-4.patch b/glibc-RHEL-47403-4.patch new file mode 100644 index 0000000..08e8cb9 --- /dev/null +++ b/glibc-RHEL-47403-4.patch @@ -0,0 +1,275 @@ +commit 9897ced8e78db5d813166a7ccccfd5a42c69ef20 +Author: Florian Weimer +Date: Fri Oct 25 16:50:10 2024 +0200 + + elf: Run constructors on cyclic recursive dlopen (bug 31986) + + This is conceptually similar to the reported bug, but does not + depend on auditing. The fix is simple: just complete execution + of the constructors. This exposed the fact that the link map + for statically linked executables does not have l_init_called + set, even though constructors have run. + + Reviewed-by: Adhemerval Zanella + +Conflicts: + elf/Makefile (fix local test re-ordering) + +diff --git a/elf/Makefile b/elf/Makefile +index b074cc29664b3e20..dc774b083eda202b 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -398,12 +398,15 @@ tests += \ + tst-dl-is_dso \ + tst-dlclose-lazy \ + tst-dlmodcount \ +- tst-dlmopen1 \ +- tst-dlmopen3 \ +- tst-dlmopen4 \ + tst-dlmopen-dlerror \ + tst-dlmopen-gethostbyname \ + tst-dlmopen-twice \ ++ tst-dlmopen1 \ ++ tst-dlmopen3 \ ++ tst-dlmopen4 \ ++ tst-dlopen-recurse \ ++ tst-dlopen-self \ ++ tst-dlopen-tlsmodid \ + tst-dlopen-tlsreinit1 \ + tst-dlopen-tlsreinit2 \ + tst-dlopen-tlsreinit3 \ +@@ -411,8 +414,6 @@ tests += \ + tst-dlopenfail \ + tst-dlopenfail-2 \ + tst-dlopenrpath \ +- tst-dlopen-self \ +- tst-dlopen-tlsmodid \ + tst-dlsym-error \ + tst-filterobj \ + tst-filterobj-dlopen \ +@@ -775,13 +776,15 @@ modules-names = \ + tst-deep1mod1 \ + tst-deep1mod2 \ + tst-deep1mod3 \ +- tst-dlmopen1mod \ + tst-dlclose-lazy-mod1 \ + tst-dlclose-lazy-mod2 \ + tst-dlmopen-dlerror-mod \ + tst-dlmopen-gethostbyname-mod \ + tst-dlmopen-twice-mod1 \ + tst-dlmopen-twice-mod2 \ ++ tst-dlmopen1mod \ ++ tst-dlopen-recursemod1 \ ++ tst-dlopen-recursemod2 \ + tst-dlopen-sgid-mod \ + tst-dlopen-tlsreinitmod1 \ + tst-dlopen-tlsreinitmod2 \ +@@ -2856,6 +2859,9 @@ tst-dlopen-tlsreinit3-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + $(objpfx)tst-dlopen-tlsreinit4.out: $(objpfx)tst-auditmod1.so + tst-dlopen-tlsreinit4-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + ++$(objpfx)tst-dlopen-recurse.out: $(objpfx)tst-dlopen-recursemod1.so ++$(objpfx)tst-dlopen-recursemod1.so: $(objpfx)tst-dlopen-recursemod2.so ++ + LDFLAGS-tst-hash-collision1-mod.so = -Wl,--hash-style=both + $(objpfx)tst-hash-collision1: $(objpfx)tst-hash-collision1-mod.so + LDFLAGS-tst-hash-collision1-mod-gnu.so = -Wl,--hash-style=gnu +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 0d2b4cd4785a226a..0b0bfb8acda28caa 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -600,6 +600,14 @@ dl_open_worker_begin (void *a) + = _dl_debug_update (args->nsid)->r_state; + assert (r_state == RT_CONSISTENT); + ++ /* Do not return without calling the (supposedly new) map's ++ constructor. This case occurs if a dependency of a directly ++ opened map has a constructor that calls dlopen again on the ++ initially opened map. The new map is initialized last, so ++ checking only it is enough. */ ++ if (!new->l_init_called) ++ _dl_catch_exception (NULL, call_dl_init, args); ++ + return; + } + +diff --git a/elf/dl-support.c b/elf/dl-support.c +index 00abc2d8056c78b0..f4dc9c61a2637f8b 100644 +--- a/elf/dl-support.c ++++ b/elf/dl-support.c +@@ -103,6 +103,7 @@ static struct link_map _dl_main_map = + .l_used = 1, + .l_tls_offset = NO_TLS_OFFSET, + .l_serial = 1, ++ .l_init_called = 1, + }; + + /* Namespace information. */ +diff --git a/elf/tst-dlopen-recurse.c b/elf/tst-dlopen-recurse.c +new file mode 100644 +index 0000000000000000..c7fb379d373c6e77 +--- /dev/null ++++ b/elf/tst-dlopen-recurse.c +@@ -0,0 +1,34 @@ ++/* Test that recursive dlopen runs constructors before return (bug 31986). ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ void *handle = xdlopen ("tst-dlopen-recursemod1.so", RTLD_NOW); ++ int *status = dlsym (handle, "recursemod1_status"); ++ printf ("info: recursemod1_status == %d (from main)\n", *status); ++ TEST_COMPARE (*status, 2); ++ xdlclose (handle); ++ return 0; ++} ++ ++#include +diff --git a/elf/tst-dlopen-recursemod1.c b/elf/tst-dlopen-recursemod1.c +new file mode 100644 +index 0000000000000000..5e0cc0eb8c32d6d4 +--- /dev/null ++++ b/elf/tst-dlopen-recursemod1.c +@@ -0,0 +1,50 @@ ++/* Directly opened test module that gets recursively opened again. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++ ++int recursemod1_status; ++ ++/* Force linking against st-dlopen-recursemod2.so. Also allows ++ checking for relocation. */ ++extern int recursemod2_status; ++int *force_recursemod2_reference = &recursemod2_status; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ ++recursemod1_status; ++ printf ("info: tst-dlopen-recursemod1.so constructor called (status %d)\n", ++ recursemod1_status); ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ /* The recursemod1_status variable was incremented in the ++ tst-dlopen-recursemod2.so constructor. */ ++ printf ("info: tst-dlopen-recursemod1.so destructor called (status %d)\n", ++ recursemod1_status); ++ if (recursemod1_status != 2) ++ { ++ puts ("error: recursemod1_status == 2 expected"); ++ exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-recursemod2.c b/elf/tst-dlopen-recursemod2.c +new file mode 100644 +index 0000000000000000..edd2f2526b877810 +--- /dev/null ++++ b/elf/tst-dlopen-recursemod2.c +@@ -0,0 +1,66 @@ ++/* Indirectly opened module that recursively opens the directly opened module. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++ ++int recursemod2_status; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ ++recursemod2_status; ++ printf ("info: tst-dlopen-recursemod2.so constructor called (status %d)\n", ++ recursemod2_status); ++ void *handle = dlopen ("tst-dlopen-recursemod1.so", RTLD_NOW); ++ if (handle == NULL) ++ { ++ printf ("error: dlopen: %s\n", dlerror ()); ++ exit (1); ++ } ++ int *status = dlsym (handle, "recursemod1_status"); ++ if (status == NULL) ++ { ++ printf ("error: dlsym: %s\n", dlerror ()); ++ exit (1); ++ } ++ printf ("info: recursemod1_status == %d\n", *status); ++ if (*status != 1) ++ { ++ puts ("error: recursemod1_status == 1 expected"); ++ exit (1); ++ } ++ ++*status; ++ printf ("info: recursemod1_status == %d\n", *status); ++ ++ int **mod2_status = dlsym (handle, "force_recursemod2_reference"); ++ if (mod2_status == NULL || *mod2_status != &recursemod2_status) ++ { ++ puts ("error: invalid recursemod2_status address in" ++ " tst-dlopen-recursemod1.so"); ++ exit (1); ++ } ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ printf ("info: tst-dlopen-recursemod2.so destructor called (status %d)\n", ++ recursemod2_status); ++} diff --git a/glibc-RHEL-47403-5.patch b/glibc-RHEL-47403-5.patch new file mode 100644 index 0000000..92e6c4d --- /dev/null +++ b/glibc-RHEL-47403-5.patch @@ -0,0 +1,103 @@ +commit e096b7a1896886eb7dd2732ccbf1184b0eec9a63 +Author: Florian Weimer +Date: Fri Oct 25 16:50:10 2024 +0200 + + elf: Signal LA_ACT_CONSISTENT to auditors after RT_CONSISTENT switch + + Auditors can call into the dynamic loader again if + LA_ACT_CONSISTENT, and those recursive calls could observe + r_state != RT_CONSISTENT. + + We should consider failing dlopen/dlmopen/dlclose if + r_state != RT_CONSISTENT. The dynamic linker is probably not + in a state in which it can handle reentrant calls. This + needs further investigation. + + Reviewed-by: Adhemerval Zanella + +Conflicts: + elf/rtld.c (kept SHARED guard downstream) + +diff --git a/elf/dl-close.c b/elf/dl-close.c +index bf699e58d753a1e2..8a4c3528a124d4e7 100644 +--- a/elf/dl-close.c ++++ b/elf/dl-close.c +@@ -759,6 +759,11 @@ _dl_close_worker (struct link_map *map, bool force) + /* TLS is cleaned up for the unloaded modules. */ + __rtld_lock_unlock_recursive (GL(dl_load_tls_lock)); + ++ /* Notify the debugger those objects are finalized and gone. */ ++ r->r_state = RT_CONSISTENT; ++ _dl_debug_state (); ++ LIBC_PROBE (unmap_complete, 2, nsid, r); ++ + #ifdef SHARED + /* Auditing checkpoint: we have deleted all objects. Also, do not notify + auditors of the cleanup of a failed audit module loading attempt. */ +@@ -771,11 +776,6 @@ _dl_close_worker (struct link_map *map, bool force) + --GL(dl_nns); + while (GL(dl_ns)[GL(dl_nns) - 1]._ns_loaded == NULL); + +- /* Notify the debugger those objects are finalized and gone. */ +- r->r_state = RT_CONSISTENT; +- _dl_debug_state (); +- LIBC_PROBE (unmap_complete, 2, nsid, r); +- + /* Recheck if we need to retry, release the lock. */ + out: + if (dl_close_state == rerun) +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 0b0bfb8acda28caa..5095ea4f96b6cf49 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -638,17 +638,17 @@ dl_open_worker_begin (void *a) + #endif + } + +-#ifdef SHARED +- /* Auditing checkpoint: we have added all objects. */ +- _dl_audit_activity_nsid (new->l_ns, LA_ACT_CONSISTENT); +-#endif +- + /* Notify the debugger all new objects are now ready to go. */ + struct r_debug *r = _dl_debug_update (args->nsid); + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + LIBC_PROBE (map_complete, 3, args->nsid, r, new); + ++#ifdef SHARED ++ /* Auditing checkpoint: we have added all objects. */ ++ _dl_audit_activity_nsid (new->l_ns, LA_ACT_CONSISTENT); ++#endif ++ + _dl_open_check (new); + + /* Print scope information. */ +diff --git a/elf/rtld.c b/elf/rtld.c +index 7d8ed0ac1188d527..cd233174c9d944b2 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -2524,11 +2524,6 @@ dl_main (const ElfW(Phdr) *phdr, + _dl_relocate_object might need to call `mprotect' for DT_TEXTREL. */ + _dl_sysdep_start_cleanup (); + +-#ifdef SHARED +- /* Auditing checkpoint: we have added all objects. */ +- _dl_audit_activity_nsid (LM_ID_BASE, LA_ACT_CONSISTENT); +-#endif +- + /* Notify the debugger all new objects are now ready to go. We must re-get + the address since by now the variable might be in another object. */ + r = _dl_debug_update (LM_ID_BASE); +@@ -2536,6 +2531,11 @@ dl_main (const ElfW(Phdr) *phdr, + _dl_debug_state (); + LIBC_PROBE (init_complete, 2, LM_ID_BASE, r); + ++#ifdef SHARED ++ /* Auditing checkpoint: we have added all objects. */ ++ _dl_audit_activity_nsid (LM_ID_BASE, LA_ACT_CONSISTENT); ++#endif ++ + #if defined USE_LDCONFIG && !defined MAP_COPY + /* We must munmap() the cache file. */ + _dl_unload_cache (); diff --git a/glibc-RHEL-47403-6.patch b/glibc-RHEL-47403-6.patch new file mode 100644 index 0000000..97e3939 --- /dev/null +++ b/glibc-RHEL-47403-6.patch @@ -0,0 +1,342 @@ +commit 43db5e2c0672cae7edea7c9685b22317eae25471 +Author: Florian Weimer +Date: Fri Oct 25 16:50:10 2024 +0200 + + elf: Signal RT_CONSISTENT after relocation processing in dlopen (bug 31986) + + Previously, a la_activity audit event was generated before + relocation processing completed. This does did not match what + happened during initial startup in elf/rtld.c (towards the end + of dl_main). It also caused various problems if an auditor + tried to open the same shared object again using dlmopen: + If it was the directly loaded object, it had a search scope + associated with it, so the early exit in dl_open_worker_begin + was taken even though the object was unrelocated. This caused + the r_state == RT_CONSISTENT assert to fail. Avoidance of the + assert also depends on reversing the order of r_state update + and auditor event (already implemented in a previous commit). + + At the later point, args->map can be NULL due to failure, + so use the assigned namespace ID instead if that is available. + + Reviewed-by: Adhemerval Zanella + +Conflicts: + elf/Makefile (fixup context) + +diff --git a/elf/Makefile b/elf/Makefile +index dc774b083eda202b..73deb69f5a3c9150 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -404,6 +404,7 @@ tests += \ + tst-dlmopen1 \ + tst-dlmopen3 \ + tst-dlmopen4 \ ++ tst-dlopen-auditdup \ + tst-dlopen-recurse \ + tst-dlopen-self \ + tst-dlopen-tlsmodid \ +@@ -783,6 +784,8 @@ modules-names = \ + tst-dlmopen-twice-mod1 \ + tst-dlmopen-twice-mod2 \ + tst-dlmopen1mod \ ++ tst-dlopen-auditdup-auditmod \ ++ tst-dlopen-auditdupmod \ + tst-dlopen-recursemod1 \ + tst-dlopen-recursemod2 \ + tst-dlopen-sgid-mod \ +@@ -2861,6 +2864,9 @@ tst-dlopen-tlsreinit4-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + + $(objpfx)tst-dlopen-recurse.out: $(objpfx)tst-dlopen-recursemod1.so + $(objpfx)tst-dlopen-recursemod1.so: $(objpfx)tst-dlopen-recursemod2.so ++tst-dlopen-auditdup-ENV = LD_AUDIT=$(objpfx)tst-dlopen-auditdup-auditmod.so ++$(objpfx)tst-dlopen-auditdup.out: \ ++ $(objpfx)tst-dlopen-auditdupmod.so $(objpfx)tst-dlopen-auditdup-auditmod.so + + LDFLAGS-tst-hash-collision1-mod.so = -Wl,--hash-style=both + $(objpfx)tst-hash-collision1: $(objpfx)tst-hash-collision1-mod.so +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 5095ea4f96b6cf49..6ec1ca033bbe7859 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -575,6 +575,14 @@ dl_open_worker_begin (void *a) + _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", + new->l_name, new->l_ns, new->l_direct_opencount); + ++#ifdef SHARED ++ /* No relocation processing on this execution path. But ++ relocation has not been performed for static ++ position-dependent executables, so disable the assert for ++ static linking. */ ++ assert (new->l_relocated); ++#endif ++ + /* If the user requested the object to be in the global + namespace but it is not so far, prepare to add it now. This + can raise an exception to do a malloc failure. */ +@@ -596,10 +604,6 @@ dl_open_worker_begin (void *a) + if ((mode & RTLD_GLOBAL) && new->l_global == 0) + add_to_global_update (new); + +- const int r_state __attribute__ ((unused)) +- = _dl_debug_update (args->nsid)->r_state; +- assert (r_state == RT_CONSISTENT); +- + /* Do not return without calling the (supposedly new) map's + constructor. This case occurs if a dependency of a directly + opened map has a constructor that calls dlopen again on the +@@ -638,17 +642,6 @@ dl_open_worker_begin (void *a) + #endif + } + +- /* Notify the debugger all new objects are now ready to go. */ +- struct r_debug *r = _dl_debug_update (args->nsid); +- r->r_state = RT_CONSISTENT; +- _dl_debug_state (); +- LIBC_PROBE (map_complete, 3, args->nsid, r, new); +- +-#ifdef SHARED +- /* Auditing checkpoint: we have added all objects. */ +- _dl_audit_activity_nsid (new->l_ns, LA_ACT_CONSISTENT); +-#endif +- + _dl_open_check (new); + + /* Print scope information. */ +@@ -695,6 +688,7 @@ dl_open_worker_begin (void *a) + created dlmopen namespaces. Do not do this for static dlopen + because libc has relocations against ld.so, which may not have + been relocated at this point. */ ++ struct r_debug *r = _dl_debug_update (args->nsid); + #ifdef SHARED + if (GL(dl_ns)[args->nsid].libc_map != NULL) + _dl_open_relocate_one_object (args, r, GL(dl_ns)[args->nsid].libc_map, +@@ -782,6 +776,26 @@ dl_open_worker (void *a) + + __rtld_lock_unlock_recursive (GL(dl_load_tls_lock)); + ++ /* Auditing checkpoint and debugger signalling. Do this even on ++ error, so that dlopen exists with consistent state. */ ++ if (args->nsid >= 0 || args->map != NULL) ++ { ++ Lmid_t nsid = args->map != NULL ? args->map->l_ns : args->nsid; ++ struct r_debug *r = _dl_debug_update (nsid); ++#ifdef SHARED ++ bool was_not_consistent = r->r_state != RT_CONSISTENT; ++#endif ++ r->r_state = RT_CONSISTENT; ++ _dl_debug_state (); ++ LIBC_PROBE (map_complete, 3, nsid, r, new); ++ ++#ifdef SHARED ++ if (was_not_consistent) ++ /* Avoid redudant/recursive signalling. */ ++ _dl_audit_activity_nsid (nsid, LA_ACT_CONSISTENT); ++#endif ++ } ++ + if (__glibc_unlikely (ex.errstring != NULL)) + /* Reraise the error. */ + _dl_signal_exception (err, &ex, NULL); +diff --git a/elf/tst-dlopen-auditdup-auditmod.c b/elf/tst-dlopen-auditdup-auditmod.c +new file mode 100644 +index 0000000000000000..9b67295e94d03e7a +--- /dev/null ++++ b/elf/tst-dlopen-auditdup-auditmod.c +@@ -0,0 +1,100 @@ ++/* Auditor that opens again an object that just has been opened. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++unsigned int ++la_version (unsigned int v) ++{ ++ return LAV_CURRENT; ++} ++ ++static bool trigger_on_la_activity; ++ ++unsigned int ++la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie) ++{ ++ printf ("info: la_objopen: \"%s\"\n", map->l_name); ++ if (strstr (map->l_name, "/tst-dlopen-auditdupmod.so") != NULL) ++ trigger_on_la_activity = true; ++ return 0; ++} ++ ++void ++la_activity (uintptr_t *cookie, unsigned int flag) ++{ ++ static unsigned int calls; ++ ++calls; ++ printf ("info: la_activity: call %u (flag %u)\n", calls, flag); ++ fflush (stdout); ++ if (trigger_on_la_activity) ++ { ++ /* Avoid triggering on the dlmopen call below. */ ++ static bool recursion; ++ if (recursion) ++ return; ++ recursion = true; ++ ++ puts ("info: about to dlmopen tst-dlopen-auditdupmod.so"); ++ fflush (stdout); ++ void *handle = dlmopen (LM_ID_BASE, "tst-dlopen-auditdupmod.so", ++ RTLD_NOW); ++ if (handle == NULL) ++ { ++ printf ("error: dlmopen: %s\n", dlerror ()); ++ fflush (stdout); ++ _exit (1); ++ } ++ ++ /* Check that the constructor has run. */ ++ int *status = dlsym (handle, "auditdupmod_status"); ++ if (status == NULL) ++ { ++ printf ("error: dlsym: %s\n", dlerror ()); ++ fflush (stdout); ++ _exit (1); ++ } ++ printf ("info: auditdupmod_status == %d\n", *status); ++ if (*status != 1) ++ { ++ puts ("error: auditdupmod_status == 1 expected"); ++ fflush (stdout); ++ _exit (1); ++ } ++ /* Checked in the destructor and the main program. */ ++ ++*status; ++ printf ("info: auditdupmod_status == %d\n", *status); ++ ++ /* Check that the module has been relocated. */ ++ int **status_address = dlsym (handle, "auditdupmod_status_address"); ++ if (status_address == NULL || *status_address != status) ++ { ++ puts ("error: invalid auditdupmod_status address in" ++ " tst-dlopen-auditdupmod.so"); ++ fflush (stdout); ++ _exit (1); ++ } ++ ++ fflush (stdout); ++ } ++} +diff --git a/elf/tst-dlopen-auditdup.c b/elf/tst-dlopen-auditdup.c +new file mode 100644 +index 0000000000000000..d022c58ae3091da1 +--- /dev/null ++++ b/elf/tst-dlopen-auditdup.c +@@ -0,0 +1,36 @@ ++/* Test that recursive dlopen from auditor works (bug 31986). ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ puts ("info: about to dlopen tst-dlopen-auditdupmod.so"); ++ fflush (stdout); ++ void *handle = xdlopen ("tst-dlopen-auditdupmod.so", RTLD_NOW); ++ int *status = xdlsym (handle, "auditdupmod_status"); ++ printf ("info: auditdupmod_status == %d (from main)\n", *status); ++ TEST_COMPARE (*status, 2); ++ xdlclose (handle); ++ return 0; ++} ++ ++#include +diff --git a/elf/tst-dlopen-auditdupmod.c b/elf/tst-dlopen-auditdupmod.c +new file mode 100644 +index 0000000000000000..59b7e21daa8212df +--- /dev/null ++++ b/elf/tst-dlopen-auditdupmod.c +@@ -0,0 +1,48 @@ ++/* Directly opened test module that gets reopened from the auditor. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++ ++int auditdupmod_status; ++ ++/* Used to check for successful relocation processing. */ ++int *auditdupmod_status_address = &auditdupmod_status; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ ++auditdupmod_status; ++ printf ("info: tst-dlopen-auditdupmod.so constructor called (status %d)\n", ++ auditdupmod_status); ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ /* The tst-dlopen-auditdup-auditmod.so auditor incremented ++ auditdupmod_status. */ ++ printf ("info: tst-dlopen-auditdupmod.so destructor called (status %d)\n", ++ auditdupmod_status); ++ if (auditdupmod_status != 2) ++ { ++ puts ("error: auditdupmod_status == 2 expected"); ++ exit (1); ++ } ++} diff --git a/glibc-RHEL-47403-7.patch b/glibc-RHEL-47403-7.patch new file mode 100644 index 0000000..a20ef02 --- /dev/null +++ b/glibc-RHEL-47403-7.patch @@ -0,0 +1,272 @@ +commit 95129e6b8fabdaa8cd8a4a5cc20be0f4cb0ba59f +Author: Florian Weimer +Date: Mon Oct 28 14:45:30 2024 +0100 + + Revert "elf: Run constructors on cyclic recursive dlopen (bug 31986)" + + This reverts commit 9897ced8e78db5d813166a7ccccfd5a42c69ef20. + + Adjust the test expectations in elf/tst-dlopen-auditdup-auditmod.c + accordingly. + +Conflicts: + elf/Makefile (fixup context) + +diff --git a/elf/Makefile b/elf/Makefile +index 73deb69f5a3c9150..a358ad7ff0eb2af7 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -405,7 +405,6 @@ tests += \ + tst-dlmopen3 \ + tst-dlmopen4 \ + tst-dlopen-auditdup \ +- tst-dlopen-recurse \ + tst-dlopen-self \ + tst-dlopen-tlsmodid \ + tst-dlopen-tlsreinit1 \ +@@ -786,8 +785,6 @@ modules-names = \ + tst-dlmopen1mod \ + tst-dlopen-auditdup-auditmod \ + tst-dlopen-auditdupmod \ +- tst-dlopen-recursemod1 \ +- tst-dlopen-recursemod2 \ + tst-dlopen-sgid-mod \ + tst-dlopen-tlsreinitmod1 \ + tst-dlopen-tlsreinitmod2 \ +@@ -2862,8 +2859,6 @@ tst-dlopen-tlsreinit3-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + $(objpfx)tst-dlopen-tlsreinit4.out: $(objpfx)tst-auditmod1.so + tst-dlopen-tlsreinit4-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so + +-$(objpfx)tst-dlopen-recurse.out: $(objpfx)tst-dlopen-recursemod1.so +-$(objpfx)tst-dlopen-recursemod1.so: $(objpfx)tst-dlopen-recursemod2.so + tst-dlopen-auditdup-ENV = LD_AUDIT=$(objpfx)tst-dlopen-auditdup-auditmod.so + $(objpfx)tst-dlopen-auditdup.out: \ + $(objpfx)tst-dlopen-auditdupmod.so $(objpfx)tst-dlopen-auditdup-auditmod.so +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 6ec1ca033bbe7859..6557c2fd7ca0bbfe 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -604,14 +604,6 @@ dl_open_worker_begin (void *a) + if ((mode & RTLD_GLOBAL) && new->l_global == 0) + add_to_global_update (new); + +- /* Do not return without calling the (supposedly new) map's +- constructor. This case occurs if a dependency of a directly +- opened map has a constructor that calls dlopen again on the +- initially opened map. The new map is initialized last, so +- checking only it is enough. */ +- if (!new->l_init_called) +- _dl_catch_exception (NULL, call_dl_init, args); +- + return; + } + +diff --git a/elf/dl-support.c b/elf/dl-support.c +index f4dc9c61a2637f8b..00abc2d8056c78b0 100644 +--- a/elf/dl-support.c ++++ b/elf/dl-support.c +@@ -103,7 +103,6 @@ static struct link_map _dl_main_map = + .l_used = 1, + .l_tls_offset = NO_TLS_OFFSET, + .l_serial = 1, +- .l_init_called = 1, + }; + + /* Namespace information. */ +diff --git a/elf/tst-dlopen-auditdup-auditmod.c b/elf/tst-dlopen-auditdup-auditmod.c +index 9b67295e94d03e7a..270a595ec4de1439 100644 +--- a/elf/tst-dlopen-auditdup-auditmod.c ++++ b/elf/tst-dlopen-auditdup-auditmod.c +@@ -66,7 +66,11 @@ la_activity (uintptr_t *cookie, unsigned int flag) + _exit (1); + } + +- /* Check that the constructor has run. */ ++ /* Check that the constructor has not run. Running the ++ constructor would require constructing its dependencies, but ++ the constructor call that triggered this auditing activity ++ has not completed, and constructors among the dependencies ++ may not be able to deal with that. */ + int *status = dlsym (handle, "auditdupmod_status"); + if (status == NULL) + { +@@ -75,9 +79,9 @@ la_activity (uintptr_t *cookie, unsigned int flag) + _exit (1); + } + printf ("info: auditdupmod_status == %d\n", *status); +- if (*status != 1) ++ if (*status != 0) + { +- puts ("error: auditdupmod_status == 1 expected"); ++ puts ("error: auditdupmod_status == 0 expected"); + fflush (stdout); + _exit (1); + } +diff --git a/elf/tst-dlopen-recurse.c b/elf/tst-dlopen-recurse.c +deleted file mode 100644 +index c7fb379d373c6e77..0000000000000000 +--- a/elf/tst-dlopen-recurse.c ++++ /dev/null +@@ -1,34 +0,0 @@ +-/* Test that recursive dlopen runs constructors before return (bug 31986). +- Copyright (C) 2024 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 +- . */ +- +-#include +-#include +-#include +- +-static int +-do_test (void) +-{ +- void *handle = xdlopen ("tst-dlopen-recursemod1.so", RTLD_NOW); +- int *status = dlsym (handle, "recursemod1_status"); +- printf ("info: recursemod1_status == %d (from main)\n", *status); +- TEST_COMPARE (*status, 2); +- xdlclose (handle); +- return 0; +-} +- +-#include +diff --git a/elf/tst-dlopen-recursemod1.c b/elf/tst-dlopen-recursemod1.c +deleted file mode 100644 +index 5e0cc0eb8c32d6d4..0000000000000000 +--- a/elf/tst-dlopen-recursemod1.c ++++ /dev/null +@@ -1,50 +0,0 @@ +-/* Directly opened test module that gets recursively opened again. +- Copyright (C) 2024 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 +- . */ +- +-#include +-#include +-#include +- +-int recursemod1_status; +- +-/* Force linking against st-dlopen-recursemod2.so. Also allows +- checking for relocation. */ +-extern int recursemod2_status; +-int *force_recursemod2_reference = &recursemod2_status; +- +-static void __attribute__ ((constructor)) +-init (void) +-{ +- ++recursemod1_status; +- printf ("info: tst-dlopen-recursemod1.so constructor called (status %d)\n", +- recursemod1_status); +-} +- +-static void __attribute__ ((destructor)) +-fini (void) +-{ +- /* The recursemod1_status variable was incremented in the +- tst-dlopen-recursemod2.so constructor. */ +- printf ("info: tst-dlopen-recursemod1.so destructor called (status %d)\n", +- recursemod1_status); +- if (recursemod1_status != 2) +- { +- puts ("error: recursemod1_status == 2 expected"); +- exit (1); +- } +-} +diff --git a/elf/tst-dlopen-recursemod2.c b/elf/tst-dlopen-recursemod2.c +deleted file mode 100644 +index edd2f2526b877810..0000000000000000 +--- a/elf/tst-dlopen-recursemod2.c ++++ /dev/null +@@ -1,66 +0,0 @@ +-/* Indirectly opened module that recursively opens the directly opened module. +- Copyright (C) 2024 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 +- . */ +- +-#include +-#include +-#include +- +-int recursemod2_status; +- +-static void __attribute__ ((constructor)) +-init (void) +-{ +- ++recursemod2_status; +- printf ("info: tst-dlopen-recursemod2.so constructor called (status %d)\n", +- recursemod2_status); +- void *handle = dlopen ("tst-dlopen-recursemod1.so", RTLD_NOW); +- if (handle == NULL) +- { +- printf ("error: dlopen: %s\n", dlerror ()); +- exit (1); +- } +- int *status = dlsym (handle, "recursemod1_status"); +- if (status == NULL) +- { +- printf ("error: dlsym: %s\n", dlerror ()); +- exit (1); +- } +- printf ("info: recursemod1_status == %d\n", *status); +- if (*status != 1) +- { +- puts ("error: recursemod1_status == 1 expected"); +- exit (1); +- } +- ++*status; +- printf ("info: recursemod1_status == %d\n", *status); +- +- int **mod2_status = dlsym (handle, "force_recursemod2_reference"); +- if (mod2_status == NULL || *mod2_status != &recursemod2_status) +- { +- puts ("error: invalid recursemod2_status address in" +- " tst-dlopen-recursemod1.so"); +- exit (1); +- } +-} +- +-static void __attribute__ ((destructor)) +-fini (void) +-{ +- printf ("info: tst-dlopen-recursemod2.so destructor called (status %d)\n", +- recursemod2_status); +-} diff --git a/glibc-RHEL-47403-8.patch b/glibc-RHEL-47403-8.patch new file mode 100644 index 0000000..159e940 --- /dev/null +++ b/glibc-RHEL-47403-8.patch @@ -0,0 +1,218 @@ +commit d604f9c500570e80febfcc6a52b63a002b466f35 +Author: Florian Weimer +Date: Tue Mar 11 15:30:52 2025 +0100 + + elf: Test dlopen (NULL, RTLD_LAZY) from an ELF constructor + + This call must not complete initialization of all shared objects + in the global scope because the ELF constructor which makes the call + likely has not finished initialization. Calling more constructors + at this point would expose those to a partially constructed + dependency. + + This completes the revert of commit 9897ced8e78db5d813166a7ccccfd5a + ("elf: Run constructors on cyclic recursive dlopen (bug 31986)"). + +Conflicts: + elf/Makefile (fixup context) + +diff --git a/elf/Makefile b/elf/Makefile +index a358ad7ff0eb2af7..3a50ca90366aec94 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -405,6 +405,7 @@ tests += \ + tst-dlmopen3 \ + tst-dlmopen4 \ + tst-dlopen-auditdup \ ++ tst-dlopen-constructor-null \ + tst-dlopen-self \ + tst-dlopen-tlsmodid \ + tst-dlopen-tlsreinit1 \ +@@ -785,6 +786,8 @@ modules-names = \ + tst-dlmopen1mod \ + tst-dlopen-auditdup-auditmod \ + tst-dlopen-auditdupmod \ ++ tst-dlopen-constructor-null-mod1 \ ++ tst-dlopen-constructor-null-mod2 \ + tst-dlopen-sgid-mod \ + tst-dlopen-tlsreinitmod1 \ + tst-dlopen-tlsreinitmod2 \ +@@ -2937,3 +2940,9 @@ $(objpfx)tst-nolink-libc-2: $(objpfx)tst-nolink-libc.o + -Wl,--dynamic-linker=$(objpfx)ld.so + $(objpfx)tst-nolink-libc-2.out: $(objpfx)tst-nolink-libc-2 $(objpfx)ld.so + $< > $@ 2>&1; $(evaluate-test) ++ ++$(objpfx)tst-dlopen-constructor-null: \ ++ $(objpfx)tst-dlopen-constructor-null-mod1.so \ ++ $(objpfx)tst-dlopen-constructor-null-mod2.so ++$(objpfx)tst-dlopen-constructor-null-mod2.so: \ ++ $(objpfx)tst-dlopen-constructor-null-mod1.so +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 6557c2fd7ca0bbfe..c225654822ee3520 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -604,6 +604,16 @@ dl_open_worker_begin (void *a) + if ((mode & RTLD_GLOBAL) && new->l_global == 0) + add_to_global_update (new); + ++ /* It is not possible to run the ELF constructor for the new ++ link map if it has not executed yet: If this dlopen call came ++ from an ELF constructor that has not put that object into a ++ consistent state, completing initialization for the entire ++ scope will expose objects that have this partially ++ constructed object among its dependencies to this ++ inconsistent state. This could happen even with a benign ++ dlopen (NULL, RTLD_LAZY) call from a constructor of an ++ initially loaded shared object. */ ++ + return; + } + +diff --git a/elf/tst-dlopen-constructor-null-mod1.c b/elf/tst-dlopen-constructor-null-mod1.c +new file mode 100644 +index 0000000000000000..70a7a0ad46a1a666 +--- /dev/null ++++ b/elf/tst-dlopen-constructor-null-mod1.c +@@ -0,0 +1,55 @@ ++/* Module calling dlopen (NULL, RTLD_LAZY) to obtain the global scope. ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++int mod1_status; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ puts ("info: tst-dlopen-constructor-null-mod1.so constructor"); ++ ++ void *handle = dlopen (NULL, RTLD_LAZY); ++ if (handle == NULL) ++ { ++ printf ("error: %s\n", dlerror ()); ++ exit (1); ++ } ++ puts ("info: dlopen returned"); ++ if (dlsym (handle, "malloc") != malloc) ++ { ++ puts ("error: dlsym did not produce expected result"); ++ exit (1); ++ } ++ dlclose (handle); ++ ++ /* Check that the second module's constructor has not executed. */ ++ if (getenv ("mod2_status") != NULL) ++ { ++ printf ("error: mod2_status environment variable set: %s\n", ++ getenv ("mod2_status")); ++ exit (1); ++ } ++ ++ /* Communicate to the second module that the constructor executed. */ ++ mod1_status = 1; ++} +diff --git a/elf/tst-dlopen-constructor-null-mod2.c b/elf/tst-dlopen-constructor-null-mod2.c +new file mode 100644 +index 0000000000000000..d6e945beaec04815 +--- /dev/null ++++ b/elf/tst-dlopen-constructor-null-mod2.c +@@ -0,0 +1,37 @@ ++/* Module whose constructor should not be invoked by dlopen (NULL, RTLD_LAZY). ++ Copyright (C) 2024 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 ++ . */ ++ ++#include ++#include ++ ++extern int mod1_status; ++int mod2_status; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ printf ("info: tst-dlopen-constructor-null-mod2.so constructor" ++ " (mod1_status=%d)", mod1_status); ++ if (!(mod1_status == 1 && mod2_status == 0)) ++ { ++ puts ("error: mod1_status == 1 && mod2_status == 0 expected"); ++ exit (1); ++ } ++ setenv ("mod2_status", "constructed", 1); ++ mod2_status = 1; ++} +diff --git a/elf/tst-dlopen-constructor-null.c b/elf/tst-dlopen-constructor-null.c +new file mode 100644 +index 0000000000000000..db90643325c5235f +--- /dev/null ++++ b/elf/tst-dlopen-constructor-null.c +@@ -0,0 +1,38 @@ ++/* Verify that dlopen (NULL, RTLD_LAZY) does not complete initialization. ++ Copyright (C) 2024 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 ++ . */ ++ ++/* This test mimics what the glvndSetupPthreads function in libglvnd ++ does. */ ++ ++#include ++#include ++ ++/* Defined and initialized in the shared objects. */ ++extern int mod1_status; ++extern int mod2_status; ++ ++static int ++do_test (void) ++{ ++ TEST_COMPARE (mod1_status, 1); ++ TEST_COMPARE (mod2_status, 1); ++ TEST_COMPARE_STRING (getenv ("mod2_status"), "constructed"); ++ return 0; ++} ++ ++#include diff --git a/glibc-RHEL-47403-9.patch b/glibc-RHEL-47403-9.patch new file mode 100644 index 0000000..8aea49b --- /dev/null +++ b/glibc-RHEL-47403-9.patch @@ -0,0 +1,24 @@ +commit ac73067cb7a328bf106ecd041c020fc61be7e087 +Author: Florian Weimer +Date: Fri Oct 25 17:41:53 2024 +0200 + + elf: Fix map_complete Systemtap probe in dl_open_worker + + The refactoring did not take the change of variable into account. + Fixes commit 43db5e2c0672cae7edea7c9685b22317eae25471 + ("elf: Signal RT_CONSISTENT after relocation processing in dlopen + (bug 31986)"). + +diff --git a/elf/dl-open.c b/elf/dl-open.c +index c225654822ee3520..1e61e402455da666 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -789,7 +789,7 @@ dl_open_worker (void *a) + #endif + r->r_state = RT_CONSISTENT; + _dl_debug_state (); +- LIBC_PROBE (map_complete, 3, nsid, r, new); ++ LIBC_PROBE (map_complete, 3, nsid, r, args->map); + + #ifdef SHARED + if (was_not_consistent) diff --git a/glibc.spec b/glibc.spec index 1acf253..fc6fbda 100644 --- a/glibc.spec +++ b/glibc.spec @@ -157,7 +157,7 @@ end \ Summary: The GNU libc libraries Name: glibc Version: %{glibcversion} -Release: 211%{?dist} +Release: 212%{?dist} # In general, GPLv2+ is used by programs, LGPLv2+ is used for # libraries. @@ -1279,6 +1279,17 @@ Patch969: glibc-RHEL-24168-19.patch Patch970: glibc-RHEL-24168-20.patch Patch971: glibc-RHEL-24168-21.patch Patch972: glibc-RHEL-24168-22.patch +Patch973: glibc-RHEL-47403-1.patch +Patch974: glibc-RHEL-47403-2.patch +Patch975: glibc-RHEL-47403-3.patch +Patch976: glibc-RHEL-47403-4.patch +Patch977: glibc-RHEL-47403-5.patch +Patch978: glibc-RHEL-47403-6.patch +Patch979: glibc-RHEL-47403-7.patch +Patch980: glibc-RHEL-47403-8.patch +Patch981: glibc-RHEL-47403-9.patch +Patch982: glibc-RHEL-47403-10.patch +Patch983: glibc-RHEL-47403-11.patch ############################################################################## # Continued list of core "glibc" package information: @@ -3276,6 +3287,10 @@ update_gconv_modules_cache () %endif %changelog +* Tue Jul 08 2025 Frédéric Bérat - 2.34-212 +- Prevented `ld.so` from asserting and crashing during audited library loads. + (RHEL-47403) + * Tue Jul 08 2025 Arjun Shankar - 2.34-211 - Improve qsort implementation (RHEL-24168)