From a293b2234e372ed2588fea6afd1ea0ddbdd19592 Mon Sep 17 00:00:00 2001 From: Patsy Griffin Date: Fri, 16 Aug 2024 11:07:42 -0400 Subject: [PATCH] elf: Clarify and invert second argument of _dl_allocate_tls_init elf: Avoid re-initializing already allocated TLS in dlopen Resolves: RHEL-36147 --- glibc-RHEL-36147-1.patch | 88 +++++++ glibc-RHEL-36147-2.patch | 526 +++++++++++++++++++++++++++++++++++++++ glibc-RHEL-36147-3.patch | 16 ++ glibc.spec | 9 +- 4 files changed, 638 insertions(+), 1 deletion(-) create mode 100644 glibc-RHEL-36147-1.patch create mode 100644 glibc-RHEL-36147-2.patch create mode 100644 glibc-RHEL-36147-3.patch diff --git a/glibc-RHEL-36147-1.patch b/glibc-RHEL-36147-1.patch new file mode 100644 index 0000000..9b66168 --- /dev/null +++ b/glibc-RHEL-36147-1.patch @@ -0,0 +1,88 @@ +commit fe06fb313bddf7e4530056897d4a706606e49377 +Author: Florian Weimer +Date: Thu Aug 1 23:31:23 2024 +0200 + + elf: Clarify and invert second argument of _dl_allocate_tls_init + + Also remove an outdated comment: _dl_allocate_tls_init is + called as part of pthread_create. + + Reviewed-by: Carlos O'Donell + +diff -Nrup a/elf/dl-tls.c b/elf/dl-tls.c +--- a/elf/dl-tls.c 2024-08-09 20:22:18.516110414 -0400 ++++ b/elf/dl-tls.c 2024-08-09 20:23:26.182493188 -0400 +@@ -553,9 +553,14 @@ _dl_resize_dtv (dtv_t *dtv, size_t max_m + /* Allocate initial TLS. RESULT should be a non-NULL pointer to storage + for the TLS space. The DTV may be resized, and so this function may + call malloc to allocate that space. The loader's GL(dl_load_tls_lock) +- is taken when manipulating global TLS-related data in the loader. */ ++ is taken when manipulating global TLS-related data in the loader. ++ ++ If MAIN_THREAD, this is the first call during process ++ initialization. In this case, TLS initialization for secondary ++ (audit) namespaces is skipped because that has already been handled ++ by dlopen. */ + void * +-_dl_allocate_tls_init (void *result, bool init_tls) ++_dl_allocate_tls_init (void *result, bool main_thread) + { + if (result == NULL) + /* The memory allocation failed. */ +@@ -634,7 +639,7 @@ _dl_allocate_tls_init (void *result, boo + because it would already be set by the audit setup. However, + subsequent thread creation would need to follow the default + behaviour. */ +- if (map->l_ns != LM_ID_BASE && !init_tls) ++ if (map->l_ns != LM_ID_BASE && main_thread) + continue; + memset (__mempcpy (dest, map->l_tls_initimage, + map->l_tls_initimage_size), '\0', +@@ -662,7 +667,7 @@ _dl_allocate_tls (void *mem) + { + return _dl_allocate_tls_init (mem == NULL + ? _dl_allocate_tls_storage () +- : allocate_dtv (mem), true); ++ : allocate_dtv (mem), false); + } + rtld_hidden_def (_dl_allocate_tls) + +diff -Nrup a/elf/rtld.c b/elf/rtld.c +--- a/elf/rtld.c 2024-08-09 20:22:18.516110414 -0400 ++++ b/elf/rtld.c 2024-08-09 20:24:17.584783701 -0400 +@@ -2478,7 +2478,7 @@ ERROR: '%s': cannot process note segment + into the main thread's TLS area, which we allocated above. + Note: thread-local variables must only be accessed after completing + the next step. */ +- _dl_allocate_tls_init (tcbp, false); ++ _dl_allocate_tls_init (tcbp, true); + + /* And finally install it for the main thread. */ + if (! tls_init_tp_called) +diff -Nrup a/nptl/allocatestack.c b/nptl/allocatestack.c +--- a/nptl/allocatestack.c 2024-08-09 20:22:17.808106408 -0400 ++++ b/nptl/allocatestack.c 2024-08-09 20:25:49.436302396 -0400 +@@ -244,7 +244,7 @@ get_cached_stack (size_t *sizep, void ** + memset (dtv, '\0', (dtv[-1].counter + 1) * sizeof (dtv_t)); + + /* Re-initialize the TLS. */ +- _dl_allocate_tls_init (TLS_TPADJ (result), true); ++ _dl_allocate_tls_init (TLS_TPADJ (result), false); + + return result; + } +diff -Nrup a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +--- a/sysdeps/generic/ldsodefs.h 2024-08-09 20:22:18.517110419 -0400 ++++ b/sysdeps/generic/ldsodefs.h 2024-08-09 20:34:20.093186159 -0400 +@@ -1185,10 +1185,8 @@ extern void _dl_get_tls_static_info (siz + + extern void _dl_allocate_static_tls (struct link_map *map) attribute_hidden; + +-/* These are internal entry points to the two halves of _dl_allocate_tls, +- only used within rtld.c itself at startup time. */ + extern void *_dl_allocate_tls_storage (void) attribute_hidden; +-extern void *_dl_allocate_tls_init (void *, bool); ++extern void *_dl_allocate_tls_init (void *result, bool main_thread); + rtld_hidden_proto (_dl_allocate_tls_init) + + /* Deallocate memory allocated with _dl_allocate_tls. */ diff --git a/glibc-RHEL-36147-2.patch b/glibc-RHEL-36147-2.patch new file mode 100644 index 0000000..7c40140 --- /dev/null +++ b/glibc-RHEL-36147-2.patch @@ -0,0 +1,526 @@ +commit 5097cd344fd243fb8deb6dec96e8073753f962f9 +Author: Florian Weimer +Date: Thu Aug 1 23:31:30 2024 +0200 + + elf: Avoid re-initializing already allocated TLS in dlopen (bug 31717) + + The old code used l_init_called as an indicator for whether TLS + initialization was complete. However, it is possible that + TLS for an object is initialized, written to, and then dlopen + for this object is called again, and l_init_called is not true at + this point. Previously, this resulted in TLS being initialized + twice, discarding any interim writes (technically introducing a + use-after-free bug even). + + This commit introduces an explicit per-object flag, l_tls_in_slotinfo. + It indicates whether _dl_add_to_slotinfo has been called for this + object. This flag is used to avoid double-initialization of TLS. + In update_tls_slotinfo, the first_static_tls micro-optimization + is removed because preserving the initalization flag for subsequent + use by the second loop for static TLS is a bit complicated, and + another per-object flag does not seem to be worth it. Furthermore, + the l_init_called flag is dropped from the second loop (for static + TLS initialization) because l_need_tls_init on its own prevents + double-initialization. + + The remaining l_init_called usage in resize_scopes and update_scopes + is just an optimization due to the use of scope_has_map, so it is + not changed in this commit. + + The isupper check ensures that libc.so.6 is TLS is not reverted. + Such a revert happens if l_need_tls_init is not cleared in + _dl_allocate_tls_init for the main_thread case, now that + l_init_called is not checked anymore in update_tls_slotinfo + in elf/dl-open.c. + + Reported-by: Jonathon Anderson + Reviewed-by: Carlos O'Donell + + Conflicts: + elf/Makefile - tests and module-names differences + elf/Makefile - add $(libdl) where needed + +diff -Nrup a/elf/Makefile b/elf/Makefile +--- a/elf/Makefile 2024-08-09 21:34:38.159713929 -0400 ++++ b/elf/Makefile 2024-08-16 13:22:32.268485608 -0400 +@@ -369,6 +369,10 @@ tests += \ + tst-dlmopen-dlerror \ + tst-dlmopen-gethostbyname \ + tst-dlmopen-twice \ ++ tst-dlopen-tlsreinit1 \ ++ tst-dlopen-tlsreinit2 \ ++ tst-dlopen-tlsreinit3 \ ++ tst-dlopen-tlsreinit4 \ + tst-dlopenfail \ + tst-dlopenfail-2 \ + tst-dlopenrpath \ +@@ -720,6 +724,9 @@ modules-names = \ + tst-dlmopen-gethostbyname-mod \ + tst-dlmopen-twice-mod1 \ + tst-dlmopen-twice-mod2 \ ++ tst-dlopen-tlsreinitmod1 \ ++ tst-dlopen-tlsreinitmod2 \ ++ tst-dlopen-tlsreinitmod3 \ + tst-dlopenfaillinkmod \ + tst-dlopenfailmod1 \ + tst-dlopenfailmod2 \ +@@ -2775,3 +2782,30 @@ $(objpfx)tst-recursive-tls.out: \ + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) + $(objpfx)tst-recursive-tlsmod%.os: tst-recursive-tlsmodN.c + $(compile-command.c) -DVAR=thread_$* -DFUNC=get_threadvar_$* ++ ++# Order matters here. The test needs the constructor for ++# tst-dlopen-tlsreinitmod2.so to be called first. ++$(objpfx)tst-dlopen-tlsreinitmod3.so: $(libdl) ++$(objpfx)tst-dlopen-tlsreinit3: $(libdl) ++$(objpfx)tst-dlopen-tlsreinitmod1.so: $(libdl) ++$(objpfx)tst-dlopen-tlsreinit1: $(libdl) ++LDFLAGS-tst-dlopen-tlsreinitmod1.so = -Wl,--no-as-needed ++$(objpfx)tst-dlopen-tlsreinitmod1.so: \ ++ $(objpfx)tst-dlopen-tlsreinitmod3.so $(objpfx)tst-dlopen-tlsreinitmod2.so ++LDFLAGS-tst-dlopen-tlsreinit2 = -Wl,--no-as-needed ++$(objpfx)tst-dlopen-tlsreinit2: \ ++ $(objpfx)tst-dlopen-tlsreinitmod3.so $(objpfx)tst-dlopen-tlsreinitmod2.so ++LDFLAGS-tst-dlopen-tlsreinit4 = -Wl,--no-as-needed ++$(objpfx)tst-dlopen-tlsreinit4: \ ++ $(objpfx)tst-dlopen-tlsreinitmod3.so $(objpfx)tst-dlopen-tlsreinitmod2.so ++# tst-dlopen-tlsreinitmod2.so is underlinked and refers to ++# tst-dlopen-tlsreinitmod3.so. The dependency is provided via ++# $(objpfx)tst-dlopen-tlsreinitmod1.so. ++tst-dlopen-tlsreinitmod2.so-no-z-defs = yes ++$(objpfx)tst-dlopen-tlsreinit.out: $(objpfx)tst-dlopen-tlsreinitmod1.so \ ++ $(objpfx)tst-dlopen-tlsreinitmod2.so $(objpfx)tst-dlopen-tlsreinitmod3.so ++# Reuse an audit module which provides ample debug logging. ++$(objpfx)tst-dlopen-tlsreinit3.out: $(objpfx)tst-auditmod1.so ++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 +diff -Nrup a/elf/dl-open.c b/elf/dl-open.c +--- a/elf/dl-open.c 2024-08-09 21:34:37.782711770 -0400 ++++ b/elf/dl-open.c 2024-08-13 16:18:12.501292054 -0400 +@@ -361,17 +361,8 @@ resize_tls_slotinfo (struct link_map *ne + { + bool any_tls = false; + for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) +- { +- struct link_map *imap = new->l_searchlist.r_list[i]; +- +- /* Only add TLS memory if this object is loaded now and +- therefore is not yet initialized. */ +- if (! imap->l_init_called && imap->l_tls_blocksize > 0) +- { +- _dl_add_to_slotinfo (imap, false); +- any_tls = true; +- } +- } ++ if (_dl_add_to_slotinfo (new->l_searchlist.r_list[i], false)) ++ any_tls = true; + return any_tls; + } + +@@ -381,22 +372,8 @@ resize_tls_slotinfo (struct link_map *ne + static void + update_tls_slotinfo (struct link_map *new) + { +- unsigned int first_static_tls = new->l_searchlist.r_nlist; + for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) +- { +- struct link_map *imap = new->l_searchlist.r_list[i]; +- +- /* Only add TLS memory if this object is loaded now and +- therefore is not yet initialized. */ +- if (! imap->l_init_called && imap->l_tls_blocksize > 0) +- { +- _dl_add_to_slotinfo (imap, true); +- +- if (imap->l_need_tls_init +- && first_static_tls == new->l_searchlist.r_nlist) +- first_static_tls = i; +- } +- } ++ _dl_add_to_slotinfo (new->l_searchlist.r_list[i], true); + + size_t newgen = GL(dl_tls_generation) + 1; + if (__glibc_unlikely (newgen == 0)) +@@ -408,13 +385,11 @@ TLS generation counter wrapped! Please + /* We need a second pass for static tls data, because + _dl_update_slotinfo must not be run while calls to + _dl_add_to_slotinfo are still pending. */ +- for (unsigned int i = first_static_tls; i < new->l_searchlist.r_nlist; ++i) ++ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) + { + struct link_map *imap = new->l_searchlist.r_list[i]; + +- if (imap->l_need_tls_init +- && ! imap->l_init_called +- && imap->l_tls_blocksize > 0) ++ if (imap->l_need_tls_init && imap->l_tls_blocksize > 0) + { + /* For static TLS we have to allocate the memory here and + now, but we can delay updating the DTV. */ +diff -Nrup a/elf/dl-tls.c b/elf/dl-tls.c +--- a/elf/dl-tls.c 2024-08-09 21:34:38.162713946 -0400 ++++ b/elf/dl-tls.c 2024-08-13 16:23:19.819877383 -0400 +@@ -633,17 +633,21 @@ _dl_allocate_tls_init (void *result, boo + some platforms use in static programs requires it. */ + dtv[map->l_tls_modid].pointer.val = dest; + +- /* Copy the initialization image and clear the BSS part. For +- audit modules or dependencies with initial-exec TLS, we can not +- set the initial TLS image on default loader initialization +- because it would already be set by the audit setup. However, +- subsequent thread creation would need to follow the default +- behaviour. */ ++ /* Copy the initialization image and clear the BSS part. ++ For audit modules or dependencies with initial-exec TLS, ++ we can not set the initial TLS image on default loader ++ initialization because it would already be set by the ++ audit setup, which uses the dlopen code and already ++ clears l_need_tls_init. Calls with !main_thread from ++ pthread_create need to initialze TLS for the current ++ thread regardless of namespace. */ + if (map->l_ns != LM_ID_BASE && main_thread) + continue; + memset (__mempcpy (dest, map->l_tls_initimage, + map->l_tls_initimage_size), '\0', + map->l_tls_blocksize - map->l_tls_initimage_size); ++ if (main_thread) ++ map->l_need_tls_init = 0; + } + + total += cnt; +@@ -1100,9 +1104,32 @@ _dl_tls_initial_modid_limit_setup (void) + } + + +-void ++/* Add module to slot information data. If DO_ADD is false, only the ++ required memory is allocated. Must be called with ++ GL (dl_load_tls_lock) acquired. If the function has already been ++ called for the link map L with !DO_ADD, then this function will not ++ raise an exception, otherwise it is possible that it encounters a ++ memory allocation failure. ++ ++ Return false if L has already been added to the slotinfo data, or ++ if L has no TLS data. If the returned value is true, L has been ++ added with this call (DO_ADD), or has been added in a previous call ++ (!DO_ADD). ++ ++ The expected usage is as follows: Call _dl_add_to_slotinfo for ++ several link maps with DO_ADD set to false, and record if any calls ++ result in a true result. If there was a true result, call ++ _dl_add_to_slotinfo again, this time with DO_ADD set to true. (For ++ simplicity, it's possible to call the function for link maps where ++ the previous result was false.) The return value from the second ++ round of calls can be ignored. If there was true result initially, ++ call _dl_update_slotinfo to update the TLS generation counter. */ ++bool + _dl_add_to_slotinfo (struct link_map *l, bool do_add) + { ++ if (l->l_tls_blocksize == 0 || l->l_tls_in_slotinfo) ++ return false; ++ + /* Now that we know the object is loaded successfully add + modules containing TLS data to the dtv info table. We + might have to increase its size. */ +@@ -1158,5 +1185,8 @@ cannot create TLS data structures")); + atomic_store_relaxed (&listp->slotinfo[idx].map, l); + atomic_store_relaxed (&listp->slotinfo[idx].gen, + GL(dl_tls_generation) + 1); ++ l->l_tls_in_slotinfo = true; + } ++ ++ return true; + } +diff -Nrup a/elf/tst-dlopen-tlsreinit1.c b/elf/tst-dlopen-tlsreinit1.c +--- a/elf/tst-dlopen-tlsreinit1.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/elf/tst-dlopen-tlsreinit1.c 2024-08-09 21:57:01.495424018 -0400 +@@ -0,0 +1,40 @@ ++/* Test that dlopen preserves already accessed TLS (bug 31717). ++ 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 ++ ++static int ++do_test (void) ++{ ++ void *handle = xdlopen ("tst-dlopen-tlsreinitmod1.so", RTLD_NOW); ++ ++ bool *tlsreinitmod3_tested = xdlsym (handle, "tlsreinitmod3_tested"); ++ TEST_VERIFY (*tlsreinitmod3_tested); ++ ++ xdlclose (handle); ++ ++ /* This crashes if the libc.so.6 TLS image has been reverted. */ ++ TEST_VERIFY (!isupper ('@')); ++ ++ return 0; ++} ++ ++#include +diff -Nrup a/elf/tst-dlopen-tlsreinit2.c b/elf/tst-dlopen-tlsreinit2.c +--- a/elf/tst-dlopen-tlsreinit2.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/elf/tst-dlopen-tlsreinit2.c 2024-08-09 21:59:14.657192089 -0400 +@@ -0,0 +1,39 @@ ++/* Test that dlopen preserves already accessed TLS (bug 31717). ++ Variant with initially-linked modules. ++ 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 ++ ++ ++static int ++do_test (void) ++{ ++ /* Defined in tst-dlopen-tlsreinitmod3.so. */ ++ extern bool tlsreinitmod3_tested; ++ TEST_VERIFY (tlsreinitmod3_tested); ++ ++ /* This crashes if the libc.so.6 TLS image has been reverted. */ ++ TEST_VERIFY (!isupper ('@')); ++ ++ return 0; ++} ++ ++#include +diff -Nrup a/elf/tst-dlopen-tlsreinit3.c b/elf/tst-dlopen-tlsreinit3.c +--- a/elf/tst-dlopen-tlsreinit3.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/elf/tst-dlopen-tlsreinit3.c 2024-08-09 22:00:43.985707332 -0400 +@@ -0,0 +1,2 @@ ++/* Same code, but run with LD_AUDIT=tst-auditmod1.so. */ ++#include "tst-dlopen-tlsreinit1.c" +diff -Nrup a/elf/tst-dlopen-tlsreinit4.c b/elf/tst-dlopen-tlsreinit4.c +--- a/elf/tst-dlopen-tlsreinit4.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/elf/tst-dlopen-tlsreinit4.c 2024-08-09 22:01:30.058973079 -0400 +@@ -0,0 +1,2 @@ ++/* Same code, but run with LD_AUDIT=tst-auditmod1.so. */ ++#include "tst-dlopen-tlsreinit2.c" +diff -Nrup a/elf/tst-dlopen-tlsreinitmod1.c b/elf/tst-dlopen-tlsreinitmod1.c +--- a/elf/tst-dlopen-tlsreinitmod1.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/elf/tst-dlopen-tlsreinitmod1.c 2024-08-09 22:02:42.337389978 -0400 +@@ -0,0 +1,20 @@ ++/* Test that dlopen preserves already accessed TLS (bug 31717), module 1. ++ 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 module triggers loading of tst-dlopen-tlsreinitmod2.so and ++ tst-dlopen-tlsreinitmod3.so. */ +diff -Nrup a/elf/tst-dlopen-tlsreinitmod2.c b/elf/tst-dlopen-tlsreinitmod2.c +--- a/elf/tst-dlopen-tlsreinitmod2.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/elf/tst-dlopen-tlsreinitmod2.c 2024-08-09 22:03:54.322805189 -0400 +@@ -0,0 +1,30 @@ ++/* Test that dlopen preserves already accessed TLS (bug 31717), module 2. ++ 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 ++ ++/* Defined in tst-dlopen-tlsreinitmod3.so. This an underlinked symbol ++ dependency. */ ++extern void call_tlsreinitmod3 (void); ++ ++static void __attribute__ ((constructor)) ++tlsreinitmod2_init (void) ++{ ++ puts ("info: constructor of tst-dlopen-tlsreinitmod2.so invoked"); ++ call_tlsreinitmod3 (); ++} +diff -Nrup a/elf/tst-dlopen-tlsreinitmod3.c b/elf/tst-dlopen-tlsreinitmod3.c +--- a/elf/tst-dlopen-tlsreinitmod3.c 1969-12-31 19:00:00.000000000 -0500 ++++ b/elf/tst-dlopen-tlsreinitmod3.c 2024-08-09 22:07:26.980031781 -0400 +@@ -0,0 +1,102 @@ ++/* Test that dlopen preserves already accessed TLS (bug 31717), module 3. ++ 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 ++ ++/* Used to verify from the main program that the test ran. */ ++bool tlsreinitmod3_tested; ++ ++/* This TLS variable must not revert back to the initial state after ++ dlopen. */ ++static __thread int tlsreinitmod3_state = 1; ++ ++/* Set from the ELF constructor during dlopen. */ ++static bool tlsreinitmod3_constructed; ++ ++/* Second half of test, behind a compiler barrier. The compiler ++ barrier is necessary to prevent carrying over TLS address ++ information from call_tlsreinitmod3 to call_tlsreinitmod3_tail. */ ++void call_tlsreinitmod3_tail (void *self) __attribute__ ((weak)); ++ ++/* Called from tst-dlopen-tlsreinitmod2.so. */ ++void ++call_tlsreinitmod3 (void) ++{ ++ printf ("info: call_tlsreinitmod3 invoked (state=%d)\n", ++ tlsreinitmod3_state); ++ ++ if (tlsreinitmod3_constructed) ++ { ++ puts ("error: call_tlsreinitmod3 called after ELF constructor"); ++ fflush (stdout); ++ /* Cannot rely on test harness due to dynamic linking. */ ++ _exit (1); ++ } ++ ++ tlsreinitmod3_state = 2; ++ ++ /* Self-dlopen. This will run the ELF constructor. */ ++ void *self = dlopen ("tst-dlopen-tlsreinitmod3.so", RTLD_NOW); ++ if (self == NULL) ++ { ++ printf ("error: dlopen: %s\n", dlerror ()); ++ fflush (stdout); ++ /* Cannot rely on test harness due to dynamic linking. */ ++ _exit (1); ++ } ++ ++ call_tlsreinitmod3_tail (self); ++} ++ ++void ++call_tlsreinitmod3_tail (void *self) ++{ ++ printf ("info: dlopen returned in tlsreinitmod3 (state=%d)\n", ++ tlsreinitmod3_state); ++ ++ if (!tlsreinitmod3_constructed) ++ { ++ puts ("error: dlopen did not call tlsreinitmod3 ELF constructor"); ++ fflush (stdout); ++ /* Cannot rely on test harness due to dynamic linking. */ ++ _exit (1); ++ } ++ ++ if (tlsreinitmod3_state != 2) ++ { ++ puts ("error: TLS state reverted in tlsreinitmod3"); ++ fflush (stdout); ++ /* Cannot rely on test harness due to dynamic linking. */ ++ _exit (1); ++ } ++ ++ dlclose (self); ++ ++ /* Signal test completion to the main program. */ ++ tlsreinitmod3_tested = true; ++} ++ ++static void __attribute__ ((constructor)) ++tlsreinitmod3_init (void) ++{ ++ puts ("info: constructor of tst-dlopen-tlsreinitmod3.so invoked"); ++ tlsreinitmod3_constructed = true; ++} +diff -Nrup a/include/link.h b/include/link.h +--- a/include/link.h 2024-08-09 21:34:37.642710968 -0400 ++++ b/include/link.h 2024-08-09 22:09:03.764585534 -0400 +@@ -210,6 +210,7 @@ struct link_map + unsigned int l_free_initfini:1; /* Nonzero if l_initfini can be + freed, ie. not allocated with + the dummy malloc in ld.so. */ ++ unsigned int l_tls_in_slotinfo:1; /* TLS slotinfo updated in dlopen. */ + + /* NODELETE status of the map. Only valid for maps of type + lt_loaded. Lazy binding sets l_nodelete_active directly, +diff -Nrup a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +--- a/sysdeps/generic/ldsodefs.h 2024-08-09 21:34:38.164713958 -0400 ++++ b/sysdeps/generic/ldsodefs.h 2024-08-13 16:41:52.398600434 -0400 +@@ -1218,13 +1218,7 @@ extern void *_dl_open (const char *name, + extern int _dl_scope_free (void *) attribute_hidden; + + +-/* Add module to slot information data. If DO_ADD is false, only the +- required memory is allocated. Must be called with GL +- (dl_load_tls_lock) acquired. If the function has already been called +- for the link map L with !do_add, then this function will not raise +- an exception, otherwise it is possible that it encounters a memory +- allocation failure. */ +-extern void _dl_add_to_slotinfo (struct link_map *l, bool do_add) ++extern bool _dl_add_to_slotinfo (struct link_map *l, bool do_add) + attribute_hidden; + + /* Update slot information data for at least the generation of the diff --git a/glibc-RHEL-36147-3.patch b/glibc-RHEL-36147-3.patch new file mode 100644 index 0000000..a2b3e76 --- /dev/null +++ b/glibc-RHEL-36147-3.patch @@ -0,0 +1,16 @@ +Author: Patsy Griffin + + Fix a typo in naming tst-dlopen-tlsreinit1.out + +diff -Nrup a/elf/Makefile b/elf/Makefile +--- a/elf/Makefile 2024-08-16 13:27:00.700921146 -0400 ++++ b/elf/Makefile 2024-08-16 13:29:12.951628406 -0400 +@@ -2802,7 +2802,7 @@ $(objpfx)tst-dlopen-tlsreinit4: \ + # tst-dlopen-tlsreinitmod3.so. The dependency is provided via + # $(objpfx)tst-dlopen-tlsreinitmod1.so. + tst-dlopen-tlsreinitmod2.so-no-z-defs = yes +-$(objpfx)tst-dlopen-tlsreinit.out: $(objpfx)tst-dlopen-tlsreinitmod1.so \ ++$(objpfx)tst-dlopen-tlsreinit1.out: $(objpfx)tst-dlopen-tlsreinitmod1.so \ + $(objpfx)tst-dlopen-tlsreinitmod2.so $(objpfx)tst-dlopen-tlsreinitmod3.so + # Reuse an audit module which provides ample debug logging. + $(objpfx)tst-dlopen-tlsreinit3.out: $(objpfx)tst-auditmod1.so diff --git a/glibc.spec b/glibc.spec index c458920..e80d62f 100644 --- a/glibc.spec +++ b/glibc.spec @@ -132,7 +132,7 @@ end \ Summary: The GNU libc libraries Name: glibc Version: %{glibcversion} -Release: %{glibcrelease}.4 +Release: %{glibcrelease}.5 # In general, GPLv2+ is used by programs, LGPLv2+ is used for # libraries. @@ -1192,6 +1192,9 @@ Patch1004: glibc-RHEL-52428-1.patch Patch1005: glibc-RHEL-52428-2.patch Patch1006: glibc-RHEL-39994-1.patch Patch1007: glibc-RHEL-39994-2.patch +Patch1008: glibc-RHEL-36147-1.patch +Patch1009: glibc-RHEL-36147-2.patch +Patch1010: glibc-RHEL-36147-3.patch ############################################################################## # Continued list of core "glibc" package information: @@ -3023,6 +3026,10 @@ fi %files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared %changelog +* Fri Aug 16 2024 Patsy Griffin - 2.28-251.5 +- elf: Clarify and invert second argument of _dl_allocate_tls_init +- elf: Avoid re-initializing already allocated TLS in dlopen (RHEL-36147) + * Thu Aug 8 2024 Patsy Griffin - 2.28-251.4 - elf: Avoid some free (NULL) calls in _dl_update_slotinfo - elf: Support recursive use of dynamic TLS in interposed malloc (RHEL-39994)