Compare commits
No commits in common. 'c9' and 'c9-beta' have entirely different histories.
@ -1,207 +0,0 @@
|
||||
Author: Charles Fol <folcharles@gmail.com>
|
||||
Date: Thu Mar 28 12:25:38 2024 -0300
|
||||
|
||||
iconv: ISO-2022-CN-EXT: fix out-of-bound writes when writing escape sequence (CVE-2024-2961)
|
||||
|
||||
ISO-2022-CN-EXT uses escape sequences to indicate character set changes
|
||||
(as specified by RFC 1922). While the SOdesignation has the expected
|
||||
bounds checks, neither SS2designation nor SS3designation have its;
|
||||
allowing a write overflow of 1, 2, or 3 bytes with fixed values:
|
||||
'$+I', '$+J', '$+K', '$+L', '$+M', or '$*H'.
|
||||
|
||||
Checked on aarch64-linux-gnu.
|
||||
|
||||
Co-authored-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
|
||||
Tested-by: Carlos O'Donell <carlos@redhat.com>
|
||||
|
||||
Conflicts:
|
||||
iconvdata/Makefile
|
||||
(usual tests conflict)
|
||||
|
||||
diff --git a/iconvdata/Makefile b/iconvdata/Makefile
|
||||
index d5507a048c6a6508..25bd004e7f92a994 100644
|
||||
--- a/iconvdata/Makefile
|
||||
+++ b/iconvdata/Makefile
|
||||
@@ -75,7 +75,7 @@ ifeq (yes,$(build-shared))
|
||||
tests = bug-iconv1 bug-iconv2 tst-loading tst-e2big tst-iconv4 bug-iconv4 \
|
||||
tst-iconv6 bug-iconv5 bug-iconv6 tst-iconv7 bug-iconv8 bug-iconv9 \
|
||||
bug-iconv10 bug-iconv11 bug-iconv12 tst-iconv-big5-hkscs-to-2ucs4 \
|
||||
- bug-iconv13 bug-iconv14 bug-iconv15
|
||||
+ bug-iconv13 bug-iconv14 bug-iconv15 tst-iconv-iso-2022-cn-ext
|
||||
ifeq ($(have-thread-library),yes)
|
||||
tests += bug-iconv3
|
||||
endif
|
||||
@@ -330,6 +330,8 @@ $(objpfx)bug-iconv14.out: $(addprefix $(objpfx), $(gconv-modules)) \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
$(objpfx)bug-iconv15.out: $(addprefix $(objpfx), $(gconv-modules)) \
|
||||
$(addprefix $(objpfx),$(modules.so))
|
||||
+$(objpfx)tst-iconv-iso-2022-cn-ext.out: $(addprefix $(objpfx), $(gconv-modules)) \
|
||||
+ $(addprefix $(objpfx),$(modules.so))
|
||||
|
||||
$(objpfx)iconv-test.out: run-iconv-test.sh \
|
||||
$(addprefix $(objpfx), $(gconv-modules)) \
|
||||
diff --git a/iconvdata/iso-2022-cn-ext.c b/iconvdata/iso-2022-cn-ext.c
|
||||
index 2aca91c021f21ba0..c1339fe933d9d1c4 100644
|
||||
--- a/iconvdata/iso-2022-cn-ext.c
|
||||
+++ b/iconvdata/iso-2022-cn-ext.c
|
||||
@@ -575,6 +575,12 @@ DIAG_IGNORE_Os_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
|
||||
{ \
|
||||
const char *escseq; \
|
||||
\
|
||||
+ if (outptr + 4 > outend) \
|
||||
+ { \
|
||||
+ result = __GCONV_FULL_OUTPUT; \
|
||||
+ break; \
|
||||
+ } \
|
||||
+ \
|
||||
assert (used == CNS11643_2_set); /* XXX */ \
|
||||
escseq = "*H"; \
|
||||
*outptr++ = ESC; \
|
||||
@@ -588,6 +594,12 @@ DIAG_IGNORE_Os_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
|
||||
{ \
|
||||
const char *escseq; \
|
||||
\
|
||||
+ if (outptr + 4 > outend) \
|
||||
+ { \
|
||||
+ result = __GCONV_FULL_OUTPUT; \
|
||||
+ break; \
|
||||
+ } \
|
||||
+ \
|
||||
assert ((used >> 5) >= 3 && (used >> 5) <= 7); \
|
||||
escseq = "+I+J+K+L+M" + ((used >> 5) - 3) * 2; \
|
||||
*outptr++ = ESC; \
|
||||
diff --git a/iconvdata/tst-iconv-iso-2022-cn-ext.c b/iconvdata/tst-iconv-iso-2022-cn-ext.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..96a8765fd5369681
|
||||
--- /dev/null
|
||||
+++ b/iconvdata/tst-iconv-iso-2022-cn-ext.c
|
||||
@@ -0,0 +1,128 @@
|
||||
+/* Verify ISO-2022-CN-EXT does not write out of the bounds.
|
||||
+ 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 <string.h>
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include <iconv.h>
|
||||
+#include <sys/mman.h>
|
||||
+
|
||||
+#include <support/xunistd.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/support.h>
|
||||
+
|
||||
+/* The test sets up a two memory page buffer with the second page marked
|
||||
+ PROT_NONE to trigger a fault if the conversion writes beyond the exact
|
||||
+ expected amount. Then we carry out various conversions and precisely
|
||||
+ place the start of the output buffer in order to trigger a SIGSEGV if the
|
||||
+ process writes anywhere between 1 and page sized bytes more (only one
|
||||
+ PROT_NONE page is setup as a canary) than expected. These tests exercise
|
||||
+ all three of the cases in ISO-2022-CN-EXT where the converter must switch
|
||||
+ character sets and may run out of buffer space while doing the
|
||||
+ operation. */
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ iconv_t cd = iconv_open ("ISO-2022-CN-EXT", "UTF-8");
|
||||
+ TEST_VERIFY_EXIT (cd != (iconv_t) -1);
|
||||
+
|
||||
+ char *ntf;
|
||||
+ size_t ntfsize;
|
||||
+ char *outbufbase;
|
||||
+ {
|
||||
+ int pgz = getpagesize ();
|
||||
+ TEST_VERIFY_EXIT (pgz > 0);
|
||||
+ ntfsize = 2 * pgz;
|
||||
+
|
||||
+ ntf = xmmap (NULL, ntfsize, PROT_READ | PROT_WRITE, MAP_PRIVATE
|
||||
+ | MAP_ANONYMOUS, -1);
|
||||
+ xmprotect (ntf + pgz, pgz, PROT_NONE);
|
||||
+
|
||||
+ outbufbase = ntf + pgz;
|
||||
+ }
|
||||
+
|
||||
+ /* Check if SOdesignation escape sequence does not trigger an OOB write. */
|
||||
+ {
|
||||
+ char inbuf[] = "\xe4\xba\xa4\xe6\x8d\xa2";
|
||||
+
|
||||
+ for (int i = 0; i < 9; i++)
|
||||
+ {
|
||||
+ char *inp = inbuf;
|
||||
+ size_t inleft = sizeof (inbuf) - 1;
|
||||
+
|
||||
+ char *outp = outbufbase - i;
|
||||
+ size_t outleft = i;
|
||||
+
|
||||
+ TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft)
|
||||
+ == (size_t) -1);
|
||||
+ TEST_COMPARE (errno, E2BIG);
|
||||
+
|
||||
+ TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Same as before for SS2designation. */
|
||||
+ {
|
||||
+ char inbuf[] = "㴽 \xe3\xb4\xbd";
|
||||
+
|
||||
+ for (int i = 0; i < 14; i++)
|
||||
+ {
|
||||
+ char *inp = inbuf;
|
||||
+ size_t inleft = sizeof (inbuf) - 1;
|
||||
+
|
||||
+ char *outp = outbufbase - i;
|
||||
+ size_t outleft = i;
|
||||
+
|
||||
+ TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft)
|
||||
+ == (size_t) -1);
|
||||
+ TEST_COMPARE (errno, E2BIG);
|
||||
+
|
||||
+ TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Same as before for SS3designation. */
|
||||
+ {
|
||||
+ char inbuf[] = "劄 \xe5\x8a\x84";
|
||||
+
|
||||
+ for (int i = 0; i < 14; i++)
|
||||
+ {
|
||||
+ char *inp = inbuf;
|
||||
+ size_t inleft = sizeof (inbuf) - 1;
|
||||
+
|
||||
+ char *outp = outbufbase - i;
|
||||
+ size_t outleft = i;
|
||||
+
|
||||
+ TEST_VERIFY_EXIT (iconv (cd, &inp, &inleft, &outp, &outleft)
|
||||
+ == (size_t) -1);
|
||||
+ TEST_COMPARE (errno, E2BIG);
|
||||
+
|
||||
+ TEST_VERIFY_EXIT (iconv (cd, NULL, NULL, NULL, NULL) == 0);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ TEST_VERIFY_EXIT (iconv_close (cd) != -1);
|
||||
+
|
||||
+ xmunmap (ntf, ntfsize);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
@ -1,31 +0,0 @@
|
||||
commit 87801a8fd06db1d654eea3e4f7626ff476a9bdaa
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Thu Apr 25 15:00:45 2024 +0200
|
||||
|
||||
CVE-2024-33599: nscd: Stack-based buffer overflow in netgroup cache (bug 31677)
|
||||
|
||||
Using alloca matches what other caches do. The request length is
|
||||
bounded by MAXKEYLEN.
|
||||
|
||||
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
|
||||
|
||||
diff --git a/nscd/netgroupcache.c b/nscd/netgroupcache.c
|
||||
index 2f71bf2999dad56b..f13a11b4c4fe3d99 100644
|
||||
--- a/nscd/netgroupcache.c
|
||||
+++ b/nscd/netgroupcache.c
|
||||
@@ -503,12 +503,13 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
= (struct indataset *) mempool_alloc (db,
|
||||
sizeof (*dataset) + req->key_len,
|
||||
1);
|
||||
- struct indataset dataset_mem;
|
||||
bool cacheable = true;
|
||||
if (__glibc_unlikely (dataset == NULL))
|
||||
{
|
||||
cacheable = false;
|
||||
- dataset = &dataset_mem;
|
||||
+ /* The alloca is safe because nscd_run_worker verfies that
|
||||
+ key_len is not larger than MAXKEYLEN. */
|
||||
+ dataset = alloca (sizeof (*dataset) + req->key_len);
|
||||
}
|
||||
|
||||
datahead_init_pos (&dataset->head, sizeof (*dataset) + req->key_len,
|
@ -1,52 +0,0 @@
|
||||
commit 7835b00dbce53c3c87bbbb1754a95fb5e58187aa
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Thu Apr 25 15:01:07 2024 +0200
|
||||
|
||||
CVE-2024-33600: nscd: Do not send missing not-found response in addgetnetgrentX (bug 31678)
|
||||
|
||||
If we failed to add a not-found response to the cache, the dataset
|
||||
point can be null, resulting in a null pointer dereference.
|
||||
|
||||
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
|
||||
|
||||
diff --git a/nscd/netgroupcache.c b/nscd/netgroupcache.c
|
||||
index f13a11b4c4fe3d99..08668e96a9fd2c77 100644
|
||||
--- a/nscd/netgroupcache.c
|
||||
+++ b/nscd/netgroupcache.c
|
||||
@@ -148,7 +148,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
/* No such service. */
|
||||
cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout,
|
||||
&key_copy);
|
||||
- goto writeout;
|
||||
+ goto maybe_cache_add;
|
||||
}
|
||||
|
||||
memset (&data, '\0', sizeof (data));
|
||||
@@ -349,7 +349,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
{
|
||||
cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout,
|
||||
&key_copy);
|
||||
- goto writeout;
|
||||
+ goto maybe_cache_add;
|
||||
}
|
||||
|
||||
total = buffilled;
|
||||
@@ -411,14 +411,12 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
}
|
||||
|
||||
if (he == NULL && fd != -1)
|
||||
- {
|
||||
- /* We write the dataset before inserting it to the database
|
||||
- since while inserting this thread might block and so would
|
||||
- unnecessarily let the receiver wait. */
|
||||
- writeout:
|
||||
+ /* We write the dataset before inserting it to the database since
|
||||
+ while inserting this thread might block and so would
|
||||
+ unnecessarily let the receiver wait. */
|
||||
writeall (fd, &dataset->resp, dataset->head.recsize);
|
||||
- }
|
||||
|
||||
+ maybe_cache_add:
|
||||
if (cacheable)
|
||||
{
|
||||
/* If necessary, we also propagate the data to disk. */
|
@ -1,53 +0,0 @@
|
||||
commit b048a482f088e53144d26a61c390bed0210f49f2
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Thu Apr 25 15:01:07 2024 +0200
|
||||
|
||||
CVE-2024-33600: nscd: Avoid null pointer crashes after notfound response (bug 31678)
|
||||
|
||||
The addgetnetgrentX call in addinnetgrX may have failed to produce
|
||||
a result, so the result variable in addinnetgrX can be NULL.
|
||||
Use db->negtimeout as the fallback value if there is no result data;
|
||||
the timeout is also overwritten below.
|
||||
|
||||
Also avoid sending a second not-found response. (The client
|
||||
disconnects after receiving the first response, so the data stream did
|
||||
not go out of sync even without this fix.) It is still beneficial to
|
||||
add the negative response to the mapping, so that the client can get
|
||||
it from there in the future, instead of going through the socket.
|
||||
|
||||
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
|
||||
|
||||
diff --git a/nscd/netgroupcache.c b/nscd/netgroupcache.c
|
||||
index 08668e96a9fd2c77..5ed16f871c433531 100644
|
||||
--- a/nscd/netgroupcache.c
|
||||
+++ b/nscd/netgroupcache.c
|
||||
@@ -512,14 +512,15 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
|
||||
datahead_init_pos (&dataset->head, sizeof (*dataset) + req->key_len,
|
||||
sizeof (innetgroup_response_header),
|
||||
- he == NULL ? 0 : dh->nreloads + 1, result->head.ttl);
|
||||
+ he == NULL ? 0 : dh->nreloads + 1,
|
||||
+ result == NULL ? db->negtimeout : result->head.ttl);
|
||||
/* Set the notfound status and timeout based on the result from
|
||||
getnetgrent. */
|
||||
- dataset->head.notfound = result->head.notfound;
|
||||
+ dataset->head.notfound = result == NULL || result->head.notfound;
|
||||
dataset->head.timeout = timeout;
|
||||
|
||||
dataset->resp.version = NSCD_VERSION;
|
||||
- dataset->resp.found = result->resp.found;
|
||||
+ dataset->resp.found = result != NULL && result->resp.found;
|
||||
/* Until we find a matching entry the result is 0. */
|
||||
dataset->resp.result = 0;
|
||||
|
||||
@@ -567,7 +568,9 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
goto out;
|
||||
}
|
||||
|
||||
- if (he == NULL)
|
||||
+ /* addgetnetgrentX may have already sent a notfound response. Do
|
||||
+ not send another one. */
|
||||
+ if (he == NULL && dataset->resp.found)
|
||||
{
|
||||
/* We write the dataset before inserting it to the database
|
||||
since while inserting this thread might block and so would
|
@ -1,383 +0,0 @@
|
||||
commit c04a21e050d64a1193a6daab872bca2528bda44b
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Thu Apr 25 15:01:07 2024 +0200
|
||||
|
||||
CVE-2024-33601, CVE-2024-33602: nscd: netgroup: Use two buffers in addgetnetgrentX (bug 31680)
|
||||
|
||||
This avoids potential memory corruption when the underlying NSS
|
||||
callback function does not use the buffer space to store all strings
|
||||
(e.g., for constant strings).
|
||||
|
||||
Instead of custom buffer management, two scratch buffers are used.
|
||||
This increases stack usage somewhat.
|
||||
|
||||
Scratch buffer allocation failure is handled by return -1
|
||||
(an invalid timeout value) instead of terminating the process.
|
||||
This fixes bug 31679.
|
||||
|
||||
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
|
||||
|
||||
diff --git a/nscd/netgroupcache.c b/nscd/netgroupcache.c
|
||||
index 5ed16f871c433531..92c9cb36fb00d72e 100644
|
||||
--- a/nscd/netgroupcache.c
|
||||
+++ b/nscd/netgroupcache.c
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
+#include <scratch_buffer.h>
|
||||
|
||||
#include "../inet/netgroup.h"
|
||||
#include "nscd.h"
|
||||
@@ -66,6 +67,16 @@ struct dataset
|
||||
char strdata[0];
|
||||
};
|
||||
|
||||
+/* Send a notfound response to FD. Always returns -1 to indicate an
|
||||
+ ephemeral error. */
|
||||
+static time_t
|
||||
+send_notfound (int fd)
|
||||
+{
|
||||
+ if (fd != -1)
|
||||
+ TEMP_FAILURE_RETRY (send (fd, ¬found, sizeof (notfound), MSG_NOSIGNAL));
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
/* Sends a notfound message and prepares a notfound dataset to write to the
|
||||
cache. Returns true if there was enough memory to allocate the dataset and
|
||||
returns the dataset in DATASETP, total bytes to write in TOTALP and the
|
||||
@@ -84,8 +95,7 @@ do_notfound (struct database_dyn *db, int fd, request_header *req,
|
||||
total = sizeof (notfound);
|
||||
timeout = time (NULL) + db->negtimeout;
|
||||
|
||||
- if (fd != -1)
|
||||
- TEMP_FAILURE_RETRY (send (fd, ¬found, total, MSG_NOSIGNAL));
|
||||
+ send_notfound (fd);
|
||||
|
||||
dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1);
|
||||
/* If we cannot permanently store the result, so be it. */
|
||||
@@ -110,11 +120,78 @@ do_notfound (struct database_dyn *db, int fd, request_header *req,
|
||||
return cacheable;
|
||||
}
|
||||
|
||||
+struct addgetnetgrentX_scratch
|
||||
+{
|
||||
+ /* This is the result that the caller should use. It can be NULL,
|
||||
+ point into buffer, or it can be in the cache. */
|
||||
+ struct dataset *dataset;
|
||||
+
|
||||
+ struct scratch_buffer buffer;
|
||||
+
|
||||
+ /* Used internally in addgetnetgrentX as a staging area. */
|
||||
+ struct scratch_buffer tmp;
|
||||
+
|
||||
+ /* Number of bytes in buffer that are actually used. */
|
||||
+ size_t buffer_used;
|
||||
+};
|
||||
+
|
||||
+static void
|
||||
+addgetnetgrentX_scratch_init (struct addgetnetgrentX_scratch *scratch)
|
||||
+{
|
||||
+ scratch->dataset = NULL;
|
||||
+ scratch_buffer_init (&scratch->buffer);
|
||||
+ scratch_buffer_init (&scratch->tmp);
|
||||
+
|
||||
+ /* Reserve space for the header. */
|
||||
+ scratch->buffer_used = sizeof (struct dataset);
|
||||
+ static_assert (sizeof (struct dataset) < sizeof (scratch->tmp.__space),
|
||||
+ "initial buffer space");
|
||||
+ memset (scratch->tmp.data, 0, sizeof (struct dataset));
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+addgetnetgrentX_scratch_free (struct addgetnetgrentX_scratch *scratch)
|
||||
+{
|
||||
+ scratch_buffer_free (&scratch->buffer);
|
||||
+ scratch_buffer_free (&scratch->tmp);
|
||||
+}
|
||||
+
|
||||
+/* Copy LENGTH bytes from S into SCRATCH. Returns NULL if SCRATCH
|
||||
+ could not be resized, otherwise a pointer to the copy. */
|
||||
+static char *
|
||||
+addgetnetgrentX_append_n (struct addgetnetgrentX_scratch *scratch,
|
||||
+ const char *s, size_t length)
|
||||
+{
|
||||
+ while (true)
|
||||
+ {
|
||||
+ size_t remaining = scratch->buffer.length - scratch->buffer_used;
|
||||
+ if (remaining >= length)
|
||||
+ break;
|
||||
+ if (!scratch_buffer_grow_preserve (&scratch->buffer))
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ char *copy = scratch->buffer.data + scratch->buffer_used;
|
||||
+ memcpy (copy, s, length);
|
||||
+ scratch->buffer_used += length;
|
||||
+ return copy;
|
||||
+}
|
||||
+
|
||||
+/* Copy S into SCRATCH, including its null terminator. Returns false
|
||||
+ if SCRATCH could not be resized. */
|
||||
+static bool
|
||||
+addgetnetgrentX_append (struct addgetnetgrentX_scratch *scratch, const char *s)
|
||||
+{
|
||||
+ if (s == NULL)
|
||||
+ s = "";
|
||||
+ return addgetnetgrentX_append_n (scratch, s, strlen (s) + 1) != NULL;
|
||||
+}
|
||||
+
|
||||
+/* Caller must initialize and free *SCRATCH. If the return value is
|
||||
+ negative, this function has sent a notfound response. */
|
||||
static time_t
|
||||
addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
const char *key, uid_t uid, struct hashentry *he,
|
||||
- struct datahead *dh, struct dataset **resultp,
|
||||
- void **tofreep)
|
||||
+ struct datahead *dh, struct addgetnetgrentX_scratch *scratch)
|
||||
{
|
||||
if (__glibc_unlikely (debug_level > 0))
|
||||
{
|
||||
@@ -133,14 +210,10 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
|
||||
char *key_copy = NULL;
|
||||
struct __netgrent data;
|
||||
- size_t buflen = MAX (1024, sizeof (*dataset) + req->key_len);
|
||||
- size_t buffilled = sizeof (*dataset);
|
||||
- char *buffer = NULL;
|
||||
size_t nentries = 0;
|
||||
size_t group_len = strlen (key) + 1;
|
||||
struct name_list *first_needed
|
||||
= alloca (sizeof (struct name_list) + group_len);
|
||||
- *tofreep = NULL;
|
||||
|
||||
if (netgroup_database == NULL
|
||||
&& !__nss_database_get (nss_database_netgroup, &netgroup_database))
|
||||
@@ -152,8 +225,6 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
}
|
||||
|
||||
memset (&data, '\0', sizeof (data));
|
||||
- buffer = xmalloc (buflen);
|
||||
- *tofreep = buffer;
|
||||
first_needed->next = first_needed;
|
||||
memcpy (first_needed->name, key, group_len);
|
||||
data.needed_groups = first_needed;
|
||||
@@ -196,8 +267,8 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
while (1)
|
||||
{
|
||||
int e;
|
||||
- status = getfct.f (&data, buffer + buffilled,
|
||||
- buflen - buffilled - req->key_len, &e);
|
||||
+ status = getfct.f (&data, scratch->tmp.data,
|
||||
+ scratch->tmp.length, &e);
|
||||
if (status == NSS_STATUS_SUCCESS)
|
||||
{
|
||||
if (data.type == triple_val)
|
||||
@@ -205,68 +276,10 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
const char *nhost = data.val.triple.host;
|
||||
const char *nuser = data.val.triple.user;
|
||||
const char *ndomain = data.val.triple.domain;
|
||||
-
|
||||
- size_t hostlen = strlen (nhost ?: "") + 1;
|
||||
- size_t userlen = strlen (nuser ?: "") + 1;
|
||||
- size_t domainlen = strlen (ndomain ?: "") + 1;
|
||||
-
|
||||
- if (nhost == NULL || nuser == NULL || ndomain == NULL
|
||||
- || nhost > nuser || nuser > ndomain)
|
||||
- {
|
||||
- const char *last = nhost;
|
||||
- if (last == NULL
|
||||
- || (nuser != NULL && nuser > last))
|
||||
- last = nuser;
|
||||
- if (last == NULL
|
||||
- || (ndomain != NULL && ndomain > last))
|
||||
- last = ndomain;
|
||||
-
|
||||
- size_t bufused
|
||||
- = (last == NULL
|
||||
- ? buffilled
|
||||
- : last + strlen (last) + 1 - buffer);
|
||||
-
|
||||
- /* We have to make temporary copies. */
|
||||
- size_t needed = hostlen + userlen + domainlen;
|
||||
-
|
||||
- if (buflen - req->key_len - bufused < needed)
|
||||
- {
|
||||
- buflen += MAX (buflen, 2 * needed);
|
||||
- /* Save offset in the old buffer. We don't
|
||||
- bother with the NULL check here since
|
||||
- we'll do that later anyway. */
|
||||
- size_t nhostdiff = nhost - buffer;
|
||||
- size_t nuserdiff = nuser - buffer;
|
||||
- size_t ndomaindiff = ndomain - buffer;
|
||||
-
|
||||
- char *newbuf = xrealloc (buffer, buflen);
|
||||
- /* Fix up the triplet pointers into the new
|
||||
- buffer. */
|
||||
- nhost = (nhost ? newbuf + nhostdiff
|
||||
- : NULL);
|
||||
- nuser = (nuser ? newbuf + nuserdiff
|
||||
- : NULL);
|
||||
- ndomain = (ndomain ? newbuf + ndomaindiff
|
||||
- : NULL);
|
||||
- *tofreep = buffer = newbuf;
|
||||
- }
|
||||
-
|
||||
- nhost = memcpy (buffer + bufused,
|
||||
- nhost ?: "", hostlen);
|
||||
- nuser = memcpy ((char *) nhost + hostlen,
|
||||
- nuser ?: "", userlen);
|
||||
- ndomain = memcpy ((char *) nuser + userlen,
|
||||
- ndomain ?: "", domainlen);
|
||||
- }
|
||||
-
|
||||
- char *wp = buffer + buffilled;
|
||||
- wp = memmove (wp, nhost ?: "", hostlen);
|
||||
- wp += hostlen;
|
||||
- wp = memmove (wp, nuser ?: "", userlen);
|
||||
- wp += userlen;
|
||||
- wp = memmove (wp, ndomain ?: "", domainlen);
|
||||
- wp += domainlen;
|
||||
- buffilled = wp - buffer;
|
||||
+ if (!(addgetnetgrentX_append (scratch, nhost)
|
||||
+ && addgetnetgrentX_append (scratch, nuser)
|
||||
+ && addgetnetgrentX_append (scratch, ndomain)))
|
||||
+ return send_notfound (fd);
|
||||
++nentries;
|
||||
}
|
||||
else
|
||||
@@ -318,8 +331,8 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
}
|
||||
else if (status == NSS_STATUS_TRYAGAIN && e == ERANGE)
|
||||
{
|
||||
- buflen *= 2;
|
||||
- *tofreep = buffer = xrealloc (buffer, buflen);
|
||||
+ if (!scratch_buffer_grow (&scratch->tmp))
|
||||
+ return send_notfound (fd);
|
||||
}
|
||||
else if (status == NSS_STATUS_RETURN
|
||||
|| status == NSS_STATUS_NOTFOUND
|
||||
@@ -352,10 +365,17 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
goto maybe_cache_add;
|
||||
}
|
||||
|
||||
- total = buffilled;
|
||||
+ /* Capture the result size without the key appended. */
|
||||
+ total = scratch->buffer_used;
|
||||
+
|
||||
+ /* Make a copy of the key. The scratch buffer must not move after
|
||||
+ this point. */
|
||||
+ key_copy = addgetnetgrentX_append_n (scratch, key, req->key_len);
|
||||
+ if (key_copy == NULL)
|
||||
+ return send_notfound (fd);
|
||||
|
||||
/* Fill in the dataset. */
|
||||
- dataset = (struct dataset *) buffer;
|
||||
+ dataset = scratch->buffer.data;
|
||||
timeout = datahead_init_pos (&dataset->head, total + req->key_len,
|
||||
total - offsetof (struct dataset, resp),
|
||||
he == NULL ? 0 : dh->nreloads + 1,
|
||||
@@ -364,11 +384,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
dataset->resp.version = NSCD_VERSION;
|
||||
dataset->resp.found = 1;
|
||||
dataset->resp.nresults = nentries;
|
||||
- dataset->resp.result_len = buffilled - sizeof (*dataset);
|
||||
-
|
||||
- assert (buflen - buffilled >= req->key_len);
|
||||
- key_copy = memcpy (buffer + buffilled, key, req->key_len);
|
||||
- buffilled += req->key_len;
|
||||
+ dataset->resp.result_len = total - sizeof (*dataset);
|
||||
|
||||
/* Now we can determine whether on refill we have to create a new
|
||||
record or not. */
|
||||
@@ -399,7 +415,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
if (__glibc_likely (newp != NULL))
|
||||
{
|
||||
/* Adjust pointer into the memory block. */
|
||||
- key_copy = (char *) newp + (key_copy - buffer);
|
||||
+ key_copy = (char *) newp + (key_copy - (char *) dataset);
|
||||
|
||||
dataset = memcpy (newp, dataset, total + req->key_len);
|
||||
cacheable = true;
|
||||
@@ -440,7 +456,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
|
||||
}
|
||||
|
||||
out:
|
||||
- *resultp = dataset;
|
||||
+ scratch->dataset = dataset;
|
||||
|
||||
return timeout;
|
||||
}
|
||||
@@ -461,6 +477,9 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
if (user != NULL)
|
||||
key = (char *) rawmemchr (key, '\0') + 1;
|
||||
const char *domain = *key++ ? key : NULL;
|
||||
+ struct addgetnetgrentX_scratch scratch;
|
||||
+
|
||||
+ addgetnetgrentX_scratch_init (&scratch);
|
||||
|
||||
if (__glibc_unlikely (debug_level > 0))
|
||||
{
|
||||
@@ -476,12 +495,8 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
group, group_len,
|
||||
db, uid);
|
||||
time_t timeout;
|
||||
- void *tofree;
|
||||
if (result != NULL)
|
||||
- {
|
||||
- timeout = result->head.timeout;
|
||||
- tofree = NULL;
|
||||
- }
|
||||
+ timeout = result->head.timeout;
|
||||
else
|
||||
{
|
||||
request_header req_get =
|
||||
@@ -490,7 +505,10 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
.key_len = group_len
|
||||
};
|
||||
timeout = addgetnetgrentX (db, -1, &req_get, group, uid, NULL, NULL,
|
||||
- &result, &tofree);
|
||||
+ &scratch);
|
||||
+ result = scratch.dataset;
|
||||
+ if (timeout < 0)
|
||||
+ goto out;
|
||||
}
|
||||
|
||||
struct indataset
|
||||
@@ -604,7 +622,7 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
|
||||
}
|
||||
|
||||
out:
|
||||
- free (tofree);
|
||||
+ addgetnetgrentX_scratch_free (&scratch);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@@ -614,11 +632,12 @@ addgetnetgrentX_ignore (struct database_dyn *db, int fd, request_header *req,
|
||||
const char *key, uid_t uid, struct hashentry *he,
|
||||
struct datahead *dh)
|
||||
{
|
||||
- struct dataset *ignore;
|
||||
- void *tofree;
|
||||
- time_t timeout = addgetnetgrentX (db, fd, req, key, uid, he, dh,
|
||||
- &ignore, &tofree);
|
||||
- free (tofree);
|
||||
+ struct addgetnetgrentX_scratch scratch;
|
||||
+ addgetnetgrentX_scratch_init (&scratch);
|
||||
+ time_t timeout = addgetnetgrentX (db, fd, req, key, uid, he, dh, &scratch);
|
||||
+ addgetnetgrentX_scratch_free (&scratch);
|
||||
+ if (timeout < 0)
|
||||
+ timeout = 0;
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@@ -662,5 +681,9 @@ readdinnetgr (struct database_dyn *db, struct hashentry *he,
|
||||
.key_len = he->len
|
||||
};
|
||||
|
||||
- return addinnetgrX (db, -1, &req, db->data + he->key, he->owner, he, dh);
|
||||
+ int timeout = addinnetgrX (db, -1, &req, db->data + he->key, he->owner,
|
||||
+ he, dh);
|
||||
+ if (timeout < 0)
|
||||
+ timeout = 0;
|
||||
+ return timeout;
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
commit afe42e935b3ee97bac9a7064157587777259c60e
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Mon Jun 3 10:49:40 2024 +0200
|
||||
|
||||
elf: Avoid some free (NULL) calls in _dl_update_slotinfo
|
||||
|
||||
This has been confirmed to work around some interposed mallocs. Here
|
||||
is a discussion of the impact test ust/libc-wrapper/test_libc-wrapper
|
||||
in lttng-tools:
|
||||
|
||||
New TLS usage in libgcc_s.so.1, compatibility impact
|
||||
<https://inbox.sourceware.org/libc-alpha/8734v1ieke.fsf@oldenburg.str.redhat.com/>
|
||||
|
||||
Reportedly, this patch also papers over a similar issue when tcmalloc
|
||||
2.9.1 is not compiled with -ftls-model=initial-exec. Of course the
|
||||
goal really should be to compile mallocs with the initial-exec TLS
|
||||
model, but this commit appears to be a useful interim workaround.
|
||||
|
||||
Fixes commit d2123d68275acc0f061e73d5f86ca504e0d5a344 ("elf: Fix slow
|
||||
tls access after dlopen [BZ #19924]").
|
||||
|
||||
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
|
||||
|
||||
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
|
||||
index 7b3dd9ab60..670dbc42fc 100644
|
||||
--- a/elf/dl-tls.c
|
||||
+++ b/elf/dl-tls.c
|
||||
@@ -819,7 +819,14 @@ _dl_update_slotinfo (unsigned long int req_modid, size_t new_gen)
|
||||
dtv entry free it. Note: this is not AS-safe. */
|
||||
/* XXX Ideally we will at some point create a memory
|
||||
pool. */
|
||||
- free (dtv[modid].pointer.to_free);
|
||||
+ /* Avoid calling free on a null pointer. Some mallocs
|
||||
+ incorrectly use dynamic TLS, and depending on how the
|
||||
+ free function was compiled, it could call
|
||||
+ __tls_get_addr before the null pointer check in the
|
||||
+ free implementation. Checking here papers over at
|
||||
+ least some dynamic TLS usage by interposed mallocs. */
|
||||
+ if (dtv[modid].pointer.to_free != NULL)
|
||||
+ free (dtv[modid].pointer.to_free);
|
||||
dtv[modid].pointer.val = TLS_DTV_UNALLOCATED;
|
||||
dtv[modid].pointer.to_free = NULL;
|
||||
|
@ -1,500 +0,0 @@
|
||||
commit 018f0fc3b818d4d1460a4e2384c24802504b1d20
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Mon Jul 1 17:42:04 2024 +0200
|
||||
|
||||
elf: Support recursive use of dynamic TLS in interposed malloc
|
||||
|
||||
It turns out that quite a few applications use bundled mallocs that
|
||||
have been built to use global-dynamic TLS (instead of the recommended
|
||||
initial-exec TLS). The previous workaround from
|
||||
commit afe42e935b3ee97bac9a7064157587777259c60e ("elf: Avoid some
|
||||
free (NULL) calls in _dl_update_slotinfo") does not fix all
|
||||
encountered cases unfortunatelly.
|
||||
|
||||
This change avoids the TLS generation update for recursive use
|
||||
of TLS from a malloc that was called during a TLS update. This
|
||||
is possible because an interposed malloc has a fixed module ID and
|
||||
TLS slot. (It cannot be unloaded.) If an initially-loaded module ID
|
||||
is encountered in __tls_get_addr and the dynamic linker is already
|
||||
in the middle of a TLS update, use the outdated DTV, thus avoiding
|
||||
another call into malloc. It's still necessary to update the
|
||||
DTV to the most recent generation, to get out of the slow path,
|
||||
which is why the check for recursion is needed.
|
||||
|
||||
The bookkeeping is done using a global counter instead of per-thread
|
||||
flag because TLS access in the dynamic linker is tricky.
|
||||
|
||||
All this will go away once the dynamic linker stops using malloc
|
||||
for TLS, likely as part of a change that pre-allocates all TLS
|
||||
during pthread_create/dlopen.
|
||||
|
||||
Fixes commit d2123d68275acc0f061e73d5f86ca504e0d5a344 ("elf: Fix slow
|
||||
tls access after dlopen [BZ #19924]").
|
||||
|
||||
Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile - tests and module-names differences
|
||||
|
||||
diff -Nrup a/elf/Makefile b/elf/Makefile
|
||||
--- a/elf/Makefile 2024-07-09 22:06:30.237752048 -0400
|
||||
+++ b/elf/Makefile 2024-07-10 14:09:02.996759220 -0400
|
||||
@@ -423,6 +423,7 @@ tests += \
|
||||
tst-nodeps2 \
|
||||
tst-noload \
|
||||
tst-null-argv \
|
||||
+ tst-recursive-tls \
|
||||
tst-relsort1 \
|
||||
tst-ro-dynamic \
|
||||
tst-rtld-run-static \
|
||||
@@ -783,6 +784,23 @@ modules-names = \
|
||||
tst-nodeps1-mod \
|
||||
tst-nodeps2-mod \
|
||||
tst-null-argv-lib \
|
||||
+ tst-recursive-tlsmallocmod \
|
||||
+ tst-recursive-tlsmod0 \
|
||||
+ tst-recursive-tlsmod1 \
|
||||
+ tst-recursive-tlsmod2 \
|
||||
+ tst-recursive-tlsmod3 \
|
||||
+ tst-recursive-tlsmod4 \
|
||||
+ tst-recursive-tlsmod5 \
|
||||
+ tst-recursive-tlsmod6 \
|
||||
+ tst-recursive-tlsmod7 \
|
||||
+ tst-recursive-tlsmod8 \
|
||||
+ tst-recursive-tlsmod9 \
|
||||
+ tst-recursive-tlsmod10 \
|
||||
+ tst-recursive-tlsmod11 \
|
||||
+ tst-recursive-tlsmod12 \
|
||||
+ tst-recursive-tlsmod13 \
|
||||
+ tst-recursive-tlsmod14 \
|
||||
+ tst-recursive-tlsmod15 \
|
||||
tst-relsort1mod1 \
|
||||
tst-relsort1mod2 \
|
||||
tst-ro-dynamic-mod \
|
||||
@@ -2725,3 +2743,11 @@ CFLAGS-tst-tlsgap-mod0.c += -mtls-dialec
|
||||
CFLAGS-tst-tlsgap-mod1.c += -mtls-dialect=gnu2
|
||||
CFLAGS-tst-tlsgap-mod2.c += -mtls-dialect=gnu2
|
||||
endif
|
||||
+
|
||||
+$(objpfx)tst-recursive-tls: $(objpfx)tst-recursive-tlsmallocmod.so
|
||||
+# More objects than DTV_SURPLUS, to trigger DTV reallocation.
|
||||
+$(objpfx)tst-recursive-tls.out: \
|
||||
+ $(patsubst %,$(objpfx)tst-recursive-tlsmod%.so, \
|
||||
+ 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_$*
|
||||
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
|
||||
index 670dbc42fc..3d221273f1 100644
|
||||
--- a/elf/dl-tls.c
|
||||
+++ b/elf/dl-tls.c
|
||||
@@ -75,6 +75,31 @@
|
||||
/* Default for dl_tls_static_optional. */
|
||||
#define OPTIONAL_TLS 512
|
||||
|
||||
+/* Used to count the number of threads currently executing dynamic TLS
|
||||
+ updates. Used to avoid recursive malloc calls in __tls_get_addr
|
||||
+ for an interposed malloc that uses global-dynamic TLS (which is not
|
||||
+ recommended); see _dl_tls_allocate_active checks. This could be a
|
||||
+ per-thread flag, but would need TLS access in the dynamic linker. */
|
||||
+unsigned int _dl_tls_threads_in_update;
|
||||
+
|
||||
+static inline void
|
||||
+_dl_tls_allocate_begin (void)
|
||||
+{
|
||||
+ atomic_fetch_add_relaxed (&_dl_tls_threads_in_update, 1);
|
||||
+}
|
||||
+
|
||||
+static inline void
|
||||
+_dl_tls_allocate_end (void)
|
||||
+{
|
||||
+ atomic_fetch_add_relaxed (&_dl_tls_threads_in_update, -1);
|
||||
+}
|
||||
+
|
||||
+static inline bool
|
||||
+_dl_tls_allocate_active (void)
|
||||
+{
|
||||
+ return atomic_load_relaxed (&_dl_tls_threads_in_update) > 0;
|
||||
+}
|
||||
+
|
||||
/* Compute the static TLS surplus based on the namespace count and the
|
||||
TLS space that can be used for optimizations. */
|
||||
static inline int
|
||||
@@ -425,12 +450,18 @@ _dl_allocate_tls_storage (void)
|
||||
size += TLS_PRE_TCB_SIZE;
|
||||
#endif
|
||||
|
||||
- /* Perform the allocation. Reserve space for the required alignment
|
||||
- and the pointer to the original allocation. */
|
||||
+ /* Reserve space for the required alignment and the pointer to the
|
||||
+ original allocation. */
|
||||
size_t alignment = GLRO (dl_tls_static_align);
|
||||
+
|
||||
+ /* Perform the allocation. */
|
||||
+ _dl_tls_allocate_begin ();
|
||||
void *allocated = malloc (size + alignment + sizeof (void *));
|
||||
if (__glibc_unlikely (allocated == NULL))
|
||||
- return NULL;
|
||||
+ {
|
||||
+ _dl_tls_allocate_end ();
|
||||
+ return NULL;
|
||||
+ }
|
||||
|
||||
/* Perform alignment and allocate the DTV. */
|
||||
#if TLS_TCB_AT_TP
|
||||
@@ -466,6 +497,8 @@ _dl_allocate_tls_storage (void)
|
||||
result = allocate_dtv (result);
|
||||
if (result == NULL)
|
||||
free (allocated);
|
||||
+
|
||||
+ _dl_tls_allocate_end ();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -483,6 +516,7 @@ _dl_resize_dtv (dtv_t *dtv, size_t max_modid)
|
||||
size_t newsize = max_modid + DTV_SURPLUS;
|
||||
size_t oldsize = dtv[-1].counter;
|
||||
|
||||
+ _dl_tls_allocate_begin ();
|
||||
if (dtv == GL(dl_initial_dtv))
|
||||
{
|
||||
/* This is the initial dtv that was either statically allocated in
|
||||
@@ -502,6 +536,7 @@ _dl_resize_dtv (dtv_t *dtv, size_t max_modid)
|
||||
if (newp == NULL)
|
||||
oom ();
|
||||
}
|
||||
+ _dl_tls_allocate_end ();
|
||||
|
||||
newp[0].counter = newsize;
|
||||
|
||||
@@ -676,7 +711,9 @@ allocate_dtv_entry (size_t alignment, size_t size)
|
||||
if (powerof2 (alignment) && alignment <= _Alignof (max_align_t))
|
||||
{
|
||||
/* The alignment is supported by malloc. */
|
||||
+ _dl_tls_allocate_begin ();
|
||||
void *ptr = malloc (size);
|
||||
+ _dl_tls_allocate_end ();
|
||||
return (struct dtv_pointer) { ptr, ptr };
|
||||
}
|
||||
|
||||
@@ -688,7 +725,10 @@ allocate_dtv_entry (size_t alignment, size_t size)
|
||||
|
||||
/* Perform the allocation. This is the pointer we need to free
|
||||
later. */
|
||||
+ _dl_tls_allocate_begin ();
|
||||
void *start = malloc (alloc_size);
|
||||
+ _dl_tls_allocate_end ();
|
||||
+
|
||||
if (start == NULL)
|
||||
return (struct dtv_pointer) {};
|
||||
|
||||
@@ -826,7 +866,11 @@ _dl_update_slotinfo (unsigned long int req_modid, size_t new_gen)
|
||||
free implementation. Checking here papers over at
|
||||
least some dynamic TLS usage by interposed mallocs. */
|
||||
if (dtv[modid].pointer.to_free != NULL)
|
||||
- free (dtv[modid].pointer.to_free);
|
||||
+ {
|
||||
+ _dl_tls_allocate_begin ();
|
||||
+ free (dtv[modid].pointer.to_free);
|
||||
+ _dl_tls_allocate_end ();
|
||||
+ }
|
||||
dtv[modid].pointer.val = TLS_DTV_UNALLOCATED;
|
||||
dtv[modid].pointer.to_free = NULL;
|
||||
|
||||
@@ -956,10 +1000,22 @@ __tls_get_addr (GET_ADDR_ARGS)
|
||||
size_t gen = atomic_load_relaxed (&GL(dl_tls_generation));
|
||||
if (__glibc_unlikely (dtv[0].counter != gen))
|
||||
{
|
||||
- /* Update DTV up to the global generation, see CONCURRENCY NOTES
|
||||
- in _dl_update_slotinfo. */
|
||||
- gen = atomic_load_acquire (&GL(dl_tls_generation));
|
||||
- return update_get_addr (GET_ADDR_PARAM, gen);
|
||||
+ if (_dl_tls_allocate_active ()
|
||||
+ && GET_ADDR_MODULE < _dl_tls_initial_modid_limit)
|
||||
+ /* This is a reentrant __tls_get_addr call, but we can
|
||||
+ satisfy it because it's an initially-loaded module ID.
|
||||
+ These TLS slotinfo slots do not change, so the
|
||||
+ out-of-date generation counter does not matter. However,
|
||||
+ if not in a TLS update, still update_get_addr below, to
|
||||
+ get off the slow path eventually. */
|
||||
+ ;
|
||||
+ else
|
||||
+ {
|
||||
+ /* Update DTV up to the global generation, see CONCURRENCY NOTES
|
||||
+ in _dl_update_slotinfo. */
|
||||
+ gen = atomic_load_acquire (&GL(dl_tls_generation));
|
||||
+ return update_get_addr (GET_ADDR_PARAM, gen);
|
||||
+ }
|
||||
}
|
||||
|
||||
void *p = dtv[GET_ADDR_MODULE].pointer.val;
|
||||
@@ -969,7 +1025,7 @@ __tls_get_addr (GET_ADDR_ARGS)
|
||||
|
||||
return (char *) p + GET_ADDR_OFFSET;
|
||||
}
|
||||
-#endif
|
||||
+#endif /* SHARED */
|
||||
|
||||
|
||||
/* Look up the module's TLS block as for __tls_get_addr,
|
||||
@@ -1018,6 +1074,25 @@ _dl_tls_get_addr_soft (struct link_map *l)
|
||||
return data;
|
||||
}
|
||||
|
||||
+size_t _dl_tls_initial_modid_limit;
|
||||
+
|
||||
+void
|
||||
+_dl_tls_initial_modid_limit_setup (void)
|
||||
+{
|
||||
+ struct dtv_slotinfo_list *listp = GL(dl_tls_dtv_slotinfo_list);
|
||||
+ size_t idx;
|
||||
+ for (idx = 0; idx < listp->len; ++idx)
|
||||
+ {
|
||||
+ struct link_map *l = listp->slotinfo[idx].map;
|
||||
+ if (l == NULL
|
||||
+ /* The object can be unloaded, so its modid can be
|
||||
+ reassociated. */
|
||||
+ || !(l->l_type == lt_executable || l->l_type == lt_library))
|
||||
+ break;
|
||||
+ }
|
||||
+ _dl_tls_initial_modid_limit = idx;
|
||||
+}
|
||||
+
|
||||
|
||||
void
|
||||
_dl_add_to_slotinfo (struct link_map *l, bool do_add)
|
||||
@@ -1050,9 +1125,11 @@ _dl_add_to_slotinfo (struct link_map *l, bool do_add)
|
||||
the first slot. */
|
||||
assert (idx == 0);
|
||||
|
||||
+ _dl_tls_allocate_begin ();
|
||||
listp = (struct dtv_slotinfo_list *)
|
||||
malloc (sizeof (struct dtv_slotinfo_list)
|
||||
+ TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo));
|
||||
+ _dl_tls_allocate_end ();
|
||||
if (listp == NULL)
|
||||
{
|
||||
/* We ran out of memory while resizing the dtv slotinfo list. */
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index e9525ea987..6352ba76c5 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -788,6 +788,8 @@ init_tls (size_t naudit)
|
||||
_dl_fatal_printf ("\
|
||||
cannot allocate TLS data structures for initial thread\n");
|
||||
|
||||
+ _dl_tls_initial_modid_limit_setup ();
|
||||
+
|
||||
/* Store for detection of the special case by __tls_get_addr
|
||||
so it knows not to pass this dtv to the normal realloc. */
|
||||
GL(dl_initial_dtv) = GET_DTV (tcbp);
|
||||
diff --git a/elf/tst-recursive-tls.c b/elf/tst-recursive-tls.c
|
||||
new file mode 100644
|
||||
index 0000000000..716d1f783a
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-recursive-tls.c
|
||||
@@ -0,0 +1,60 @@
|
||||
+/* Test with interposed malloc with dynamic TLS.
|
||||
+ 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 <array_length.h>
|
||||
+#include <stdio.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+
|
||||
+/* Defined in tst-recursive-tlsmallocmod.so. */
|
||||
+extern __thread unsigned int malloc_subsytem_counter;
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ /* 16 is large enough to exercise the DTV resizing case. */
|
||||
+ void *handles[16];
|
||||
+
|
||||
+ for (unsigned int i = 0; i < array_length (handles); ++i)
|
||||
+ {
|
||||
+ /* Re-use the TLS slot for module 0. */
|
||||
+ if (i > 0)
|
||||
+ xdlclose (handles[0]);
|
||||
+
|
||||
+ char soname[30];
|
||||
+ snprintf (soname, sizeof (soname), "tst-recursive-tlsmod%u.so", i);
|
||||
+ handles[i] = xdlopen (soname, RTLD_NOW);
|
||||
+
|
||||
+ if (i > 0)
|
||||
+ {
|
||||
+ handles[0] = xdlopen ("tst-recursive-tlsmod0.so", RTLD_NOW);
|
||||
+ int (*fptr) (void) = xdlsym (handles[0], "get_threadvar_0");
|
||||
+ /* May trigger TLS storage allocation using malloc. */
|
||||
+ TEST_COMPARE (fptr (), 0);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for (unsigned int i = 0; i < array_length (handles); ++i)
|
||||
+ xdlclose (handles[i]);
|
||||
+
|
||||
+ printf ("info: malloc subsystem calls: %u\n", malloc_subsytem_counter);
|
||||
+ TEST_VERIFY (malloc_subsytem_counter > 0);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
diff --git a/elf/tst-recursive-tlsmallocmod.c b/elf/tst-recursive-tlsmallocmod.c
|
||||
new file mode 100644
|
||||
index 0000000000..c24e9945d1
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-recursive-tlsmallocmod.c
|
||||
@@ -0,0 +1,64 @@
|
||||
+/* Interposed malloc with dynamic TLS.
|
||||
+ 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 <stdlib.h>
|
||||
+#include <dlfcn.h>
|
||||
+
|
||||
+__thread unsigned int malloc_subsytem_counter;
|
||||
+
|
||||
+static __typeof (malloc) *malloc_fptr;
|
||||
+static __typeof (free) *free_fptr;
|
||||
+static __typeof (calloc) *calloc_fptr;
|
||||
+static __typeof (realloc) *realloc_fptr;
|
||||
+
|
||||
+static void __attribute__ ((constructor))
|
||||
+init (void)
|
||||
+{
|
||||
+ malloc_fptr = dlsym (RTLD_NEXT, "malloc");
|
||||
+ free_fptr = dlsym (RTLD_NEXT, "free");
|
||||
+ calloc_fptr = dlsym (RTLD_NEXT, "calloc");
|
||||
+ realloc_fptr = dlsym (RTLD_NEXT, "realloc");
|
||||
+}
|
||||
+
|
||||
+void *
|
||||
+malloc (size_t size)
|
||||
+{
|
||||
+ ++malloc_subsytem_counter;
|
||||
+ return malloc_fptr (size);
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+free (void *ptr)
|
||||
+{
|
||||
+ ++malloc_subsytem_counter;
|
||||
+ return free_fptr (ptr);
|
||||
+}
|
||||
+
|
||||
+void *
|
||||
+calloc (size_t a, size_t b)
|
||||
+{
|
||||
+ ++malloc_subsytem_counter;
|
||||
+ return calloc_fptr (a, b);
|
||||
+}
|
||||
+
|
||||
+void *
|
||||
+realloc (void *ptr, size_t size)
|
||||
+{
|
||||
+ ++malloc_subsytem_counter;
|
||||
+ return realloc_fptr (ptr, size);
|
||||
+}
|
||||
diff --git a/elf/tst-recursive-tlsmodN.c b/elf/tst-recursive-tlsmodN.c
|
||||
new file mode 100644
|
||||
index 0000000000..bb7592aee6
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-recursive-tlsmodN.c
|
||||
@@ -0,0 +1,28 @@
|
||||
+/* Test module with global-dynamic TLS. Used to trigger DTV reallocation.
|
||||
+ 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/>. */
|
||||
+
|
||||
+/* Compiled with VAR and FUNC set via -D. FUNC requires some
|
||||
+ relocation against TLS variable VAR. */
|
||||
+
|
||||
+__thread int VAR;
|
||||
+
|
||||
+int
|
||||
+FUNC (void)
|
||||
+{
|
||||
+ return VAR;
|
||||
+}
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index 50f58a60e3..656e8a3fa0 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -1256,6 +1256,20 @@ extern struct link_map *_dl_update_slotinfo (unsigned long int req_modid,
|
||||
size_t gen)
|
||||
attribute_hidden;
|
||||
|
||||
+/* The last TLS module ID that is initially loaded, plus 1. TLS
|
||||
+ addresses for modules with IDs lower than that can be obtained from
|
||||
+ the DTV even if its generation is outdated. */
|
||||
+extern size_t _dl_tls_initial_modid_limit attribute_hidden attribute_relro;
|
||||
+
|
||||
+/* Compute _dl_tls_initial_modid_limit. To be called after initial
|
||||
+ relocation. */
|
||||
+void _dl_tls_initial_modid_limit_setup (void) attribute_hidden;
|
||||
+
|
||||
+/* Number of threads currently in a TLS update. This is used to
|
||||
+ detect reentrant __tls_get_addr calls without a per-thread
|
||||
+ flag. */
|
||||
+extern unsigned int _dl_tls_threads_in_update attribute_hidden;
|
||||
+
|
||||
/* Look up the module's TLS block as for __tls_get_addr,
|
||||
but never touch anything. Return null if it's not allocated yet. */
|
||||
extern void *_dl_tls_get_addr_soft (struct link_map *l) attribute_hidden;
|
||||
diff --git a/sysdeps/x86_64/dl-tls.c b/sysdeps/x86_64/dl-tls.c
|
||||
index 869023bbba..b3c1e4fcd7 100644
|
||||
--- a/sysdeps/x86_64/dl-tls.c
|
||||
+++ b/sysdeps/x86_64/dl-tls.c
|
||||
@@ -41,7 +41,10 @@ __tls_get_addr_slow (GET_ADDR_ARGS)
|
||||
dtv_t *dtv = THREAD_DTV ();
|
||||
|
||||
size_t gen = atomic_load_acquire (&GL(dl_tls_generation));
|
||||
- if (__glibc_unlikely (dtv[0].counter != gen))
|
||||
+ if (__glibc_unlikely (dtv[0].counter != gen)
|
||||
+ /* See comment in __tls_get_addr in elf/dl-tls.c. */
|
||||
+ && !(_dl_tls_allocate_active ()
|
||||
+ && GET_ADDR_MODULE < _dl_tls_initial_modid_limit))
|
||||
return update_get_addr (GET_ADDR_PARAM, gen);
|
||||
|
||||
return tls_get_addr_tail (GET_ADDR_PARAM, dtv, NULL);
|
@ -1,88 +0,0 @@
|
||||
commit fe06fb313bddf7e4530056897d4a706606e49377
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
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 <carlos@redhat.com>
|
||||
|
||||
diff -Nrup a/elf/dl-tls.c b/elf/dl-tls.c
|
||||
--- a/elf/dl-tls.c 2024-08-21 13:11:01.422983765 -0400
|
||||
+++ b/elf/dl-tls.c 2024-08-21 12:51:17.789904690 -0400
|
||||
@@ -558,9 +558,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. */
|
||||
@@ -639,7 +644,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',
|
||||
@@ -667,7 +672,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-21 13:11:01.422983765 -0400
|
||||
+++ b/elf/rtld.c 2024-08-21 12:57:33.134783030 -0400
|
||||
@@ -2485,7 +2485,7 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
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-21 13:11:00.640979593 -0400
|
||||
+++ b/nptl/allocatestack.c 2024-08-21 12:51:17.789904690 -0400
|
||||
@@ -137,7 +137,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-21 13:11:01.423983770 -0400
|
||||
+++ b/sysdeps/generic/ldsodefs.h 2024-08-21 13:30:39.072207595 -0400
|
||||
@@ -1258,10 +1258,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. */
|
@ -1,524 +0,0 @@
|
||||
commit 5097cd344fd243fb8deb6dec96e8073753f962f9
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
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 <janderson@rice.edu>
|
||||
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile - usual tests list differences
|
||||
|
||||
diff -Nrup a/elf/Makefile b/elf/Makefile
|
||||
--- a/elf/Makefile 2024-08-21 14:24:25.311070834 -0400
|
||||
+++ b/elf/Makefile 2024-08-21 14:16:19.905503937 -0400
|
||||
@@ -399,6 +399,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 \
|
||||
@@ -753,6 +757,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 \
|
||||
@@ -2751,3 +2758,26 @@ $(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.
|
||||
+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-21 14:24:25.273070633 -0400
|
||||
+++ b/elf/dl-open.c 2024-08-21 14:04:52.834912875 -0400
|
||||
@@ -362,17 +362,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;
|
||||
}
|
||||
|
||||
@@ -382,22 +373,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))
|
||||
@@ -409,13 +386,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-21 14:24:25.315070855 -0400
|
||||
+++ b/elf/dl-tls.c 2024-08-21 14:20:34.569850644 -0400
|
||||
@@ -638,17 +638,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;
|
||||
@@ -1105,9 +1109,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. */
|
||||
@@ -1163,7 +1190,10 @@ 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;
|
||||
}
|
||||
|
||||
#if THREAD_GSCOPE_IN_TCB
|
||||
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-21 14:04:52.834912875 -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
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdbool.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+#include <ctype.h>
|
||||
+
|
||||
+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 <support/test-driver.c>
|
||||
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-21 14:04:52.834912875 -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
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <ctype.h>
|
||||
+#include <stdbool.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+
|
||||
+
|
||||
+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 <support/test-driver.c>
|
||||
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-21 14:04:52.834912875 -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-21 14:04:52.834912875 -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-21 14:04:52.834912875 -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
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+/* 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-21 14:04:52.835912880 -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
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+
|
||||
+/* 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-21 14:04:52.835912880 -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
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <dlfcn.h>
|
||||
+#include <stdbool.h>
|
||||
+#include <stdio.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+/* 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-21 14:24:24.556066841 -0400
|
||||
+++ b/include/link.h 2024-08-21 14:23:54.674908823 -0400
|
||||
@@ -211,6 +211,8 @@ struct link_map
|
||||
freed, ie. not allocated with
|
||||
the dummy malloc in ld.so. */
|
||||
unsigned int l_ld_readonly:1; /* Nonzero if dynamic section is readonly. */
|
||||
+ 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-21 14:24:25.317070865 -0400
|
||||
+++ b/sysdeps/generic/ldsodefs.h 2024-08-21 14:04:52.835912880 -0400
|
||||
@@ -1291,13 +1291,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
|
@ -1,16 +0,0 @@
|
||||
Author: Patsy Griffin <patsy@redhat.com>
|
||||
|
||||
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
|
Loading…
Reference in new issue