Prevented ld.so from asserting and crashing during audited library loads.

- Backport: Extend struct r_debug to support multiple namespaces [BZ
  #15971]
- Backport: elf: Include <sysdep.h> in elf/dl-debug-symbols.S
- Backport: Minor: don't call _dl_debug_update (which can have side
  effects) inside assert
- Backport: elf: Run constructors on cyclic recursive dlopen (bug
  31986)
- Backport: elf: Signal LA_ACT_CONSISTENT to auditors after
  RT_CONSISTENT switch
- Backport: elf: Signal RT_CONSISTENT after relocation processing in
  dlopen (bug 31986)
- Backport: Revert "elf: Run constructors on cyclic recursive dlopen
  (bug 31986)"
- Backport: elf: Test dlopen (NULL, RTLD_LAZY) from an ELF constructor
- Backport: elf: Fix map_complete Systemtap probe in dl_open_worker
- Backport: elf: Introduce separate _r_debug_array variable
- Add downstream patch to keep the ABI stable

Resolves: RHEL-47403
This commit is contained in:
Frédéric Bérat 2025-07-02 14:51:13 +02:00
parent b8e117b06d
commit 72524e00c3
12 changed files with 2045 additions and 1 deletions

562
glibc-RHEL-47403-1.patch Normal file
View File

@ -0,0 +1,562 @@
commit a93d9e03a31ec14405cb3a09aa95413b67067380
Author: H.J. Lu <hjl.tools@gmail.com>
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 <fweimer@redhat.com>
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 <link.h>
+
+--
+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
+ <https://www.gnu.org/licenses/>. */
+
+#include <rtld-sizes.h>
+
+/* 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 <bits/elfclass.h> /* Defines __ELF_NATIVE_CLASS. */
#include <bits/link.h>
-/* 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <link.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnu/lib-names.h>
+#include <support/xdlfcn.h>
+#include <support/check.h>
+#include <support/test-driver.h>
+
+#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 <support/test-driver.c>
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

158
glibc-RHEL-47403-10.patch Normal file
View File

@ -0,0 +1,158 @@
commit 7278d11f3a0cd528188c719bab75575b0aea2c6e
Author: Florian Weimer <fweimer@redhat.com>
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 <hjl.tools@gmail.com>
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;

17
glibc-RHEL-47403-11.patch Normal file
View File

@ -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;

26
glibc-RHEL-47403-2.patch Normal file
View File

@ -0,0 +1,26 @@
commit 7e84ac3a3ac9e7c4dc10de2ce65db971b9650e4d
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Sep 20 15:50:00 2021 +0200
elf: Include <sysdep.h> in elf/dl-debug-symbols.S
This is necessary to generate assembler marker sections on some
targets.
Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
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 @@
<https://www.gnu.org/licenses/>. */
#include <rtld-sizes.h>
+#include <sysdep.h>
+
+/* 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. */

32
glibc-RHEL-47403-3.patch Normal file
View File

@ -0,0 +1,32 @@
commit 1b5e65ef6a442fdccf88d43c3048f98292d85631
Author: Paul Pluzhnikov <ppluzhnikov@google.com>
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));

275
glibc-RHEL-47403-4.patch Normal file
View File

@ -0,0 +1,275 @@
commit 9897ced8e78db5d813166a7ccccfd5a42c69ef20
Author: Florian Weimer <fweimer@redhat.com>
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 <adhemerval.zanella@linaro.org>
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
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+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 <support/test-driver.c>
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
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/xdlfcn.h>
+
+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
+ <https://www.gnu.org/licenses/>. */
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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);
+}

103
glibc-RHEL-47403-5.patch Normal file
View File

@ -0,0 +1,103 @@
commit e096b7a1896886eb7dd2732ccbf1184b0eec9a63
Author: Florian Weimer <fweimer@redhat.com>
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 <adhemerval.zanella@linaro.org>
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 ();

342
glibc-RHEL-47403-6.patch Normal file
View File

@ -0,0 +1,342 @@
commit 43db5e2c0672cae7edea7c9685b22317eae25471
Author: Florian Weimer <fweimer@redhat.com>
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 <adhemerval.zanella@linaro.org>
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
+ <https://www.gnu.org/licenses/>. */
+
+#include <dlfcn.h>
+#include <link.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+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
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+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 <support/test-driver.c>
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
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/xdlfcn.h>
+
+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);
+ }
+}

272
glibc-RHEL-47403-7.patch Normal file
View File

@ -0,0 +1,272 @@
commit 95129e6b8fabdaa8cd8a4a5cc20be0f4cb0ba59f
Author: Florian Weimer <fweimer@redhat.com>
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
- <https://www.gnu.org/licenses/>. */
-
-#include <stdio.h>
-#include <support/check.h>
-#include <support/xdlfcn.h>
-
-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 <support/test-driver.c>
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
- <https://www.gnu.org/licenses/>. */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <support/xdlfcn.h>
-
-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
- <https://www.gnu.org/licenses/>. */
-
-#include <dlfcn.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-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);
-}

218
glibc-RHEL-47403-8.patch Normal file
View File

@ -0,0 +1,218 @@
commit d604f9c500570e80febfcc6a52b63a002b466f35
Author: Florian Weimer <fweimer@redhat.com>
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
+ <https://www.gnu.org/licenses/>. */
+
+#include <dlfcn.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+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
+ <https://www.gnu.org/licenses/>. */
+
+/* This test mimics what the glvndSetupPthreads function in libglvnd
+ does. */
+
+#include <stdlib.h>
+#include <support/check.h>
+
+/* 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 <support/test-driver.c>

24
glibc-RHEL-47403-9.patch Normal file
View File

@ -0,0 +1,24 @@
commit ac73067cb7a328bf106ecd041c020fc61be7e087
Author: Florian Weimer <fweimer@redhat.com>
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)

View File

@ -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 <fberat@redhat.com> - 2.34-212
- Prevented `ld.so` from asserting and crashing during audited library loads.
(RHEL-47403)
* Tue Jul 08 2025 Arjun Shankar <arjun@redhat.com> - 2.34-211
- Improve qsort implementation (RHEL-24168)