Apply patch from upstream BZ #32470

This commit is contained in:
Koichiro Iwao 2025-04-28 15:39:33 +00:00 committed by root
commit 4c4fd57221
15 changed files with 1957 additions and 2 deletions

View File

@ -0,0 +1,29 @@
commit 8754a4133e154ca853e6765a3fe5c7a904c77626
Author: Joseph Myers <joseph@codesourcery.com>
Date: Fri May 26 15:03:31 2023 +0000
Add AT_RSEQ_* from Linux 6.3 to elf.h
Linux 6.3 adds constants AT_RSEQ_FEATURE_SIZE and AT_RSEQ_ALIGN; add
them to glibc's elf.h. (Recall that, although elf.h is a
system-independent header, so far we've put AT_* constants there even
if Linux-specific, as discussed in bug 15794. So rather than making
any attempt to fix that issue, the new constants are just added there
alongside the existing ones.)
Tested for x86_64.
diff --git a/elf/elf.h b/elf/elf.h
index 4738dfa28f6549fc..076d8e3f696c58f7 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -1205,6 +1205,9 @@ typedef struct
#define AT_HWCAP2 26 /* More machine-dependent hints about
processor capabilities. */
+#define AT_RSEQ_FEATURE_SIZE 27 /* rseq supported feature size. */
+#define AT_RSEQ_ALIGN 28 /* rseq allocation alignment. */
+
#define AT_EXECFN 31 /* Filename of executable. */
/* Pointer to the global system page used for system calls and other

View File

@ -0,0 +1,152 @@
commit 2b92982e2369d292560793bee8e730f695f48ff3
Author: Michael Jeanson <mjeanson@efficios.com>
Date: Wed Jul 3 12:35:34 2024 -0400
nptl: fix potential merge of __rseq_* relro symbols
While working on a patch to add support for the extensible rseq ABI, we
came across an issue where a new 'const' variable would be merged with
the existing '__rseq_size' variable. We tracked this to the use of
'-fmerge-all-constants' which allows the compiler to merge identical
constant variables. This means that all 'const' variables in a compile
unit that are of the same size and are initialized to the same value can
be merged.
In this specific case, on 32 bit systems 'unsigned int' and 'ptrdiff_t'
are both 4 bytes and initialized to 0 which should trigger the merge.
However for reasons we haven't delved into when the attribute 'section
(".data.rel.ro")' is added to the mix, only variables of the same exact
types are merged. As far as we know this behavior is not specified
anywhere and could change with a new compiler version, hence this patch.
Move the definitions of these variables into an assembler file and add
hidden writable aliases for internal use. This has the added bonus of
removing the asm workaround to set the values on rseq registration.
Tested on Debian 12 with GCC 12.2.
Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Reviewed-by: Florian Weimer <fweimer@redhat.com>
diff --git a/elf/Makefile b/elf/Makefile
index ffadf1c61f66bef8..5043684babf50a06 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -69,6 +69,7 @@ dl-routines = \
dl-printf \
dl-profile \
dl-reloc \
+ dl-rseq-symbols \
dl-runtime \
dl-scope \
dl-setup_hash \
diff --git a/elf/dl-rseq-symbols.S b/elf/dl-rseq-symbols.S
new file mode 100644
index 0000000000000000..b4bba06a99b0a486
--- /dev/null
+++ b/elf/dl-rseq-symbols.S
@@ -0,0 +1,64 @@
+/* Define symbols used by rseq.
+ 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 <sysdep.h>
+
+#if __WORDSIZE == 64
+#define RSEQ_OFFSET_SIZE 8
+#else
+#define RSEQ_OFFSET_SIZE 4
+#endif
+
+/* Some targets define a macro to denote the zero register. */
+#undef zero
+
+/* Define 2 symbols: '__rseq_size' is public const and '_rseq_size' (an
+ alias of '__rseq_size') is hidden and writable for internal use by the
+ dynamic linker which will initialize the value both symbols point to
+ before copy relocations take place. */
+
+ .globl __rseq_size
+ .type __rseq_size, %object
+ .size __rseq_size, 4
+ .hidden _rseq_size
+ .globl _rseq_size
+ .type _rseq_size, %object
+ .size _rseq_size, 4
+ .section .data.rel.ro
+ .balign 4
+__rseq_size:
+_rseq_size:
+ .zero 4
+
+/* Define 2 symbols: '__rseq_offset' is public const and '_rseq_offset' (an
+ alias of '__rseq_offset') is hidden and writable for internal use by the
+ dynamic linker which will initialize the value both symbols point to
+ before copy relocations take place. */
+
+ .globl __rseq_offset
+ .type __rseq_offset, %object
+ .size __rseq_offset, RSEQ_OFFSET_SIZE
+ .hidden _rseq_offset
+ .globl _rseq_offset
+ .type _rseq_offset, %object
+ .size _rseq_offset, RSEQ_OFFSET_SIZE
+ .section .data.rel.ro
+ .balign RSEQ_OFFSET_SIZE
+__rseq_offset:
+_rseq_offset:
+ .zero RSEQ_OFFSET_SIZE
diff --git a/sysdeps/nptl/dl-tls_init_tp.c b/sysdeps/nptl/dl-tls_init_tp.c
index d5f2587f1348441c..039080d7110f5064 100644
--- a/sysdeps/nptl/dl-tls_init_tp.c
+++ b/sysdeps/nptl/dl-tls_init_tp.c
@@ -45,8 +45,10 @@ rtld_mutex_dummy (pthread_mutex_t *lock)
#endif
const unsigned int __rseq_flags;
-const unsigned int __rseq_size attribute_relro;
-const ptrdiff_t __rseq_offset attribute_relro;
+
+/* The variables are in .data.relro but are not yet write-protected. */
+extern unsigned int _rseq_size attribute_hidden;
+extern ptrdiff_t _rseq_offset attribute_hidden;
void
__tls_pre_init_tp (void)
@@ -107,10 +109,7 @@ __tls_init_tp (void)
#endif
if (rseq_register_current_thread (pd, do_rseq))
{
- /* We need a writable view of the variables. They are in
- .data.relro and are not yet write-protected. */
- extern unsigned int size __asm__ ("__rseq_size");
- size = sizeof (pd->rseq_area);
+ _rseq_size = sizeof (pd->rseq_area);
}
#ifdef RSEQ_SIG
@@ -119,8 +118,7 @@ __tls_init_tp (void)
all targets support __thread_pointer, so set __rseq_offset only
if thre rseq registration may have happened because RSEQ_SIG is
defined. */
- extern ptrdiff_t offset __asm__ ("__rseq_offset");
- offset = (char *) &pd->rseq_area - (char *) __thread_pointer ();
+ _rseq_offset = (char *) &pd->rseq_area - (char *) __thread_pointer ();
#endif
}

View File

@ -0,0 +1,136 @@
commit 2e456ccf0c34a056e3ccafac4a0c7effef14d918
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Jul 8 21:14:00 2024 +0200
Linux: Make __rseq_size useful for feature detection (bug 31965)
The __rseq_size value is now the active area of struct rseq
(so 20 initially), not the full struct size including padding
at the end (32 initially).
Update misc/tst-rseq to print some additional diagnostics.
Reviewed-by: Michael Jeanson <mjeanson@efficios.com>
Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
diff --git a/manual/threads.texi b/manual/threads.texi
index 48fd562923800b34..fcf9e2bba5abd02e 100644
--- a/manual/threads.texi
+++ b/manual/threads.texi
@@ -1020,8 +1020,12 @@ This variable is either zero (if restartable sequence registration
failed or has been disabled) or the size of the restartable sequence
registration. This can be different from the size of @code{struct rseq}
if the kernel has extended the size of the registration. If
-registration is successful, @code{__rseq_size} is at least 32 (the
-initial size of @code{struct rseq}).
+registration is successful, @code{__rseq_size} is at least 20 (the
+initially active size of @code{struct rseq}).
+
+Previous versions of @theglibc{} set this to 32 even if the kernel only
+supported the initial area of 20 bytes because the value included unused
+padding at the end of the restartable sequence area.
@end deftypevar
@deftypevar {unsigned int} __rseq_flags
diff --git a/sysdeps/nptl/dl-tls_init_tp.c b/sysdeps/nptl/dl-tls_init_tp.c
index 039080d7110f5064..2f56281a02246a09 100644
--- a/sysdeps/nptl/dl-tls_init_tp.c
+++ b/sysdeps/nptl/dl-tls_init_tp.c
@@ -46,10 +46,6 @@ rtld_mutex_dummy (pthread_mutex_t *lock)
const unsigned int __rseq_flags;
-/* The variables are in .data.relro but are not yet write-protected. */
-extern unsigned int _rseq_size attribute_hidden;
-extern ptrdiff_t _rseq_offset attribute_hidden;
-
void
__tls_pre_init_tp (void)
{
@@ -108,9 +104,7 @@ __tls_init_tp (void)
do_rseq = TUNABLE_GET (rseq, int, NULL);
#endif
if (rseq_register_current_thread (pd, do_rseq))
- {
- _rseq_size = sizeof (pd->rseq_area);
- }
+ _rseq_size = RSEQ_AREA_SIZE_INITIAL_USED;
#ifdef RSEQ_SIG
/* This should be a compile-time constant, but the current
diff --git a/sysdeps/unix/sysv/linux/rseq-internal.h b/sysdeps/unix/sysv/linux/rseq-internal.h
index 9e8f99fd51a063b1..ccb16640133fa9e3 100644
--- a/sysdeps/unix/sysv/linux/rseq-internal.h
+++ b/sysdeps/unix/sysv/linux/rseq-internal.h
@@ -25,15 +25,34 @@
#include <stdio.h>
#include <sys/rseq.h>
+/* 32 is the initially required value for the area size. The
+ actually used rseq size may be less (20 bytes initially). */
+#define RSEQ_AREA_SIZE_INITIAL 32
+#define RSEQ_AREA_SIZE_INITIAL_USED 20
+
+/* The variables are in .data.relro but are not yet write-protected. */
+extern unsigned int _rseq_size attribute_hidden;
+extern ptrdiff_t _rseq_offset attribute_hidden;
+
#ifdef RSEQ_SIG
static inline bool
rseq_register_current_thread (struct pthread *self, bool do_rseq)
{
if (do_rseq)
{
+ unsigned int size;
+#if IS_IN (rtld)
+ /* Use the hidden symbol in ld.so. */
+ size = _rseq_size;
+#else
+ size = __rseq_size;
+#endif
+ if (size < RSEQ_AREA_SIZE_INITIAL)
+ /* The initial implementation used only 20 bytes out of 32,
+ but still expected size 32. */
+ size = RSEQ_AREA_SIZE_INITIAL;
int ret = INTERNAL_SYSCALL_CALL (rseq, &self->rseq_area,
- sizeof (self->rseq_area),
- 0, RSEQ_SIG);
+ size, 0, RSEQ_SIG);
if (!INTERNAL_SYSCALL_ERROR_P (ret))
return true;
}
diff --git a/sysdeps/unix/sysv/linux/tst-rseq.c b/sysdeps/unix/sysv/linux/tst-rseq.c
index 572c11166f8b6533..1d404db610c08fdf 100644
--- a/sysdeps/unix/sysv/linux/tst-rseq.c
+++ b/sysdeps/unix/sysv/linux/tst-rseq.c
@@ -29,6 +29,7 @@
# include <stdlib.h>
# include <string.h>
# include <syscall.h>
+# include <sys/auxv.h>
# include <thread_pointer.h>
# include <tls.h>
# include "tst-rseq.h"
@@ -42,7 +43,8 @@ do_rseq_main_test (void)
TEST_COMPARE (__rseq_flags, 0);
TEST_VERIFY ((char *) __thread_pointer () + __rseq_offset
== (char *) &pd->rseq_area);
- TEST_COMPARE (__rseq_size, sizeof (pd->rseq_area));
+ /* The current implementation only supports the initial size. */
+ TEST_COMPARE (__rseq_size, 20);
}
static void
@@ -52,6 +54,12 @@ do_rseq_test (void)
{
FAIL_UNSUPPORTED ("kernel does not support rseq, skipping test");
}
+ printf ("info: __rseq_size: %u\n", __rseq_size);
+ printf ("info: __rseq_offset: %td\n", __rseq_offset);
+ printf ("info: __rseq_flags: %u\n", __rseq_flags);
+ printf ("info: getauxval (AT_RSEQ_FEATURE_SIZE): %ld\n",
+ getauxval (AT_RSEQ_FEATURE_SIZE));
+ printf ("info: getauxval (AT_RSEQ_ALIGN): %ld\n", getauxval (AT_RSEQ_ALIGN));
do_rseq_main_test ();
}
#else /* RSEQ_SIG */

View File

@ -0,0 +1,54 @@
commit 97f60abd25628425971f07e9b0e7f8eec0741235
Author: Michael Jeanson <mjeanson@efficios.com>
Date: Thu Nov 7 22:23:49 2024 +0100
nptl: initialize rseq area prior to registration
Per the rseq syscall documentation, 3 fields are required to be
initialized by userspace prior to registration, they are 'cpu_id',
'rseq_cs' and 'flags'. Since we have no guarantee that 'struct pthread'
is cleared on all architectures, explicitly set those 3 fields prior to
registration.
Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
Reviewed-by: Florian Weimer <fweimer@redhat.com>
diff --git a/nptl/descr.h b/nptl/descr.h
index 6484e3703f9a0a97..c08c5149c7239e7c 100644
--- a/nptl/descr.h
+++ b/nptl/descr.h
@@ -424,6 +424,8 @@ struct pthread
{
uint32_t cpu_id_start;
uint32_t cpu_id;
+ uint64_t rseq_cs;
+ uint32_t flags;
};
char pad[32]; /* Original rseq area size. */
} rseq_area __attribute__ ((aligned (32)));
diff --git a/sysdeps/unix/sysv/linux/rseq-internal.h b/sysdeps/unix/sysv/linux/rseq-internal.h
index ccb16640133fa9e3..7e70ae7ebdee4917 100644
--- a/sysdeps/unix/sysv/linux/rseq-internal.h
+++ b/sysdeps/unix/sysv/linux/rseq-internal.h
@@ -51,11 +51,21 @@ rseq_register_current_thread (struct pthread *self, bool do_rseq)
/* The initial implementation used only 20 bytes out of 32,
but still expected size 32. */
size = RSEQ_AREA_SIZE_INITIAL;
+
+ /* Initialize the rseq fields that are read by the kernel on
+ registration, there is no guarantee that struct pthread is
+ cleared on all architectures. */
+ THREAD_SETMEM (self, rseq_area.cpu_id, RSEQ_CPU_ID_UNINITIALIZED);
+ THREAD_SETMEM (self, rseq_area.rseq_cs, 0);
+ THREAD_SETMEM (self, rseq_area.flags, 0);
+
int ret = INTERNAL_SYSCALL_CALL (rseq, &self->rseq_area,
size, 0, RSEQ_SIG);
if (!INTERNAL_SYSCALL_ERROR_P (ret))
return true;
}
+ /* When rseq is disabled by tunables or the registration fails, inform
+ userspace by setting 'cpu_id' to RSEQ_CPU_ID_REGISTRATION_FAILED. */
THREAD_SETMEM (self, rseq_area.cpu_id, RSEQ_CPU_ID_REGISTRATION_FAILED);
return false;
}

View File

@ -0,0 +1,28 @@
commit d9f40387d3305d97e30a8cf8724218c42a63680a
Author: Michael Jeanson <mjeanson@efficios.com>
Date: Wed Nov 20 14:15:42 2024 -0500
nptl: initialize cpu_id_start prior to rseq registration
When adding explicit initialization of rseq fields prior to
registration, I glossed over the fact that 'cpu_id_start' is also
documented as initialized by user-space.
While current kernels don't validate the content of this field on
registration, future ones could.
Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
diff --git a/sysdeps/unix/sysv/linux/rseq-internal.h b/sysdeps/unix/sysv/linux/rseq-internal.h
index 7e70ae7ebdee4917..c108a12a6227eeac 100644
--- a/sysdeps/unix/sysv/linux/rseq-internal.h
+++ b/sysdeps/unix/sysv/linux/rseq-internal.h
@@ -56,6 +56,7 @@ rseq_register_current_thread (struct pthread *self, bool do_rseq)
registration, there is no guarantee that struct pthread is
cleared on all architectures. */
THREAD_SETMEM (self, rseq_area.cpu_id, RSEQ_CPU_ID_UNINITIALIZED);
+ THREAD_SETMEM (self, rseq_area.cpu_id_start, 0);
THREAD_SETMEM (self, rseq_area.rseq_cs, 0);
THREAD_SETMEM (self, rseq_area.flags, 0);

View File

@ -0,0 +1,32 @@
commit 4f20a1dc5242fb4bb8763e0451df898fa48e740c
Author: Martin Sebor <msebor@redhat.com>
Date: Tue Jan 25 17:39:36 2022 -0700
stdlib: Avoid -Wuse-after-free in __add_to_environ [BZ #26779]
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/stdlib/setenv.c b/stdlib/setenv.c
index c3d2cee7b6..2176cbac31 100644
--- a/stdlib/setenv.c
+++ b/stdlib/setenv.c
@@ -150,7 +150,9 @@ __add_to_environ (const char *name, const char *value, const char *combined,
{
char **new_environ;
- /* We allocated this space; we can extend it. */
+ /* We allocated this space; we can extend it. Avoid using the raw
+ reallocated pointer to avoid GCC -Wuse-after-free. */
+ uintptr_t ip_last_environ = (uintptr_t)last_environ;
new_environ = (char **) realloc (last_environ,
(size + 2) * sizeof (char *));
if (new_environ == NULL)
@@ -159,7 +161,7 @@ __add_to_environ (const char *name, const char *value, const char *combined,
return -1;
}
- if (__environ != last_environ)
+ if ((uintptr_t)__environ != ip_last_environ)
memcpy ((char *) new_environ, (char *) __environ,
size * sizeof (char *));

View File

@ -0,0 +1,98 @@
commit a6ccce23afc2a09a17ac2a86a2b726b58df609df
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Thu Feb 9 10:36:57 2023 -0300
stdlib: Simplify getenv
And remove _STRING_ARCH_unaligned usage.
Checked on x86_64-linux-gnu and i686-linux-gnu.
Reviewed-by: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
diff --git a/stdlib/getenv.c b/stdlib/getenv.c
index e3157ce2f3..8408e641a6 100644
--- a/stdlib/getenv.c
+++ b/stdlib/getenv.c
@@ -15,76 +15,22 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <endian.h>
-#include <errno.h>
-#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-
-/* Return the value of the environment variable NAME. This implementation
- is tuned a bit in that it assumes no environment variable has an empty
- name which of course should always be true. We have a special case for
- one character names so that for the general case we can assume at least
- two characters which we can access. By doing this we can avoid using the
- `strncmp' most of the time. */
char *
getenv (const char *name)
{
- char **ep;
- uint16_t name_start;
-
if (__environ == NULL || name[0] == '\0')
return NULL;
- if (name[1] == '\0')
- {
- /* The name of the variable consists of only one character. Therefore
- the first two characters of the environment entry are this character
- and a '=' character. */
-#if __BYTE_ORDER == __LITTLE_ENDIAN || !_STRING_ARCH_unaligned
- name_start = ('=' << 8) | *(const unsigned char *) name;
-#else
- name_start = '=' | ((*(const unsigned char *) name) << 8);
-#endif
- for (ep = __environ; *ep != NULL; ++ep)
- {
-#if _STRING_ARCH_unaligned
- uint16_t ep_start = *(uint16_t *) *ep;
-#else
- uint16_t ep_start = (((unsigned char *) *ep)[0]
- | (((unsigned char *) *ep)[1] << 8));
-#endif
- if (name_start == ep_start)
- return &(*ep)[2];
- }
- }
- else
+ size_t len = strlen (name);
+ for (char **ep = __environ; *ep != NULL; ++ep)
{
- size_t len = strlen (name);
-#if _STRING_ARCH_unaligned
- name_start = *(const uint16_t *) name;
-#else
- name_start = (((const unsigned char *) name)[0]
- | (((const unsigned char *) name)[1] << 8));
-#endif
- len -= 2;
- name += 2;
-
- for (ep = __environ; *ep != NULL; ++ep)
- {
-#if _STRING_ARCH_unaligned
- uint16_t ep_start = *(uint16_t *) *ep;
-#else
- uint16_t ep_start = (((unsigned char *) *ep)[0]
- | (((unsigned char *) *ep)[1] << 8));
-#endif
-
- if (name_start == ep_start && !strncmp (*ep + 2, name, len)
- && (*ep)[len + 2] == '=')
- return &(*ep)[len + 3];
- }
+ if (name[0] == (*ep)[0]
+ && strncmp (name, *ep, len) == 0 && (*ep)[len] == '=')
+ return *ep + len + 1;
}
return NULL;

View File

@ -0,0 +1,75 @@
commit 9401024e5e6be0e1c3870e185daae865cd4501f4
Author: Joe Simmons-Talbott <josimmon@redhat.com>
Date: Fri Jun 30 14:31:45 2023 +0000
setenv.c: Get rid of alloca.
Use malloc rather than alloca to avoid potential stack overflow.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/stdlib/setenv.c b/stdlib/setenv.c
index ba5257d3bf..cc71287fcc 100644
--- a/stdlib/setenv.c
+++ b/stdlib/setenv.c
@@ -182,18 +182,11 @@ __add_to_environ (const char *name, const char *value, const char *combined,
{
const size_t varlen = namelen + 1 + vallen;
#ifdef USE_TSEARCH
- char *new_value;
- int use_alloca = __libc_use_alloca (varlen);
- if (__builtin_expect (use_alloca, 1))
- new_value = (char *) alloca (varlen);
- else
+ char *new_value = malloc (varlen);
+ if (new_value == NULL)
{
- new_value = malloc (varlen);
- if (new_value == NULL)
- {
- UNLOCK;
- return -1;
- }
+ UNLOCK;
+ return -1;
}
# ifdef _LIBC
__mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
@@ -209,35 +202,14 @@ __add_to_environ (const char *name, const char *value, const char *combined,
#endif
{
#ifdef USE_TSEARCH
- if (__glibc_unlikely (! use_alloca))
- np = new_value;
- else
+ np = new_value;
#endif
- {
- np = malloc (varlen);
- if (__glibc_unlikely (np == NULL))
- {
- UNLOCK;
- return -1;
- }
-
-#ifdef USE_TSEARCH
- memcpy (np, new_value, varlen);
-#else
- memcpy (np, name, namelen);
- np[namelen] = '=';
- memcpy (&np[namelen + 1], value, vallen);
-#endif
- }
/* And remember the value. */
STORE_VALUE (np);
}
#ifdef USE_TSEARCH
else
- {
- if (__glibc_unlikely (! use_alloca))
- free (new_value);
- }
+ free (new_value);
#endif
}

View File

@ -0,0 +1,882 @@
commit 7a61e7f557a97ab597d6fca5e2d1f13f65685c61
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Nov 21 21:10:52 2024 +0100
stdlib: Make getenv thread-safe in more cases
Async-signal-safety is preserved, too. In fact, getenv is fully
reentrant and can be called from the malloc call in setenv
(if a replacement malloc uses getenv during its initialization).
This is relatively easy to implement because even before this change,
setenv, unsetenv, clearenv, putenv do not deallocate the environment
strings themselves as they are removed from the environment.
The main changes are:
* Use release stores for environment array updates, following
the usual pattern for safely publishing immutable data
(in this case, the environment strings).
* Do not deallocate the environment array. Instead, keep older
versions around and adopt an exponential resizing policy. This
results in an amortized constant space leak per active environment
variable, but there already is such a leak for the variable itself
(and that is even length-dependent, and includes no-longer used
values).
* Add a seqlock-like mechanism to retry getenv if a concurrent
unsetenv is observed. Without that, it is possible that
getenv returns NULL for a variable that is never unset. This
is visible on some AArch64 implementations with the newly
added stdlib/tst-getenv-unsetenv test case. The mechanism
is not a pure seqlock because it tolerates one write from
unsetenv. This avoids the need for a second copy of the
environ array that getenv can read from a signal handler
that happens to interrupt an unsetenv call.
No manual updates are included with this patch because environ
usage with execve, posix_spawn, system is still not thread-safe
relative unsetenv. The new process may end up with an environment
that misses entries that were never unset. This is the same issue
described above for getenv.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff -Nrup a/stdlib/Makefile b/stdlib/Makefile
--- a/stdlib/Makefile 2025-02-26 21:42:43.588147025 -0500
+++ b/stdlib/Makefile 2025-02-26 21:51:09.401674391 -0500
@@ -95,6 +95,9 @@ tests := \
tst-canon-bz26341 \
tst-cxa_atexit \
tst-environ \
+ tst-getenv-signal \
+ tst-getenv-thread \
+ tst-getenv-unsetenv \
tst-getrandom \
tst-limits \
tst-makecontext \
@@ -302,3 +305,7 @@ $(objpfx)tst-setcontext3.out: tst-setcon
'$(run-program-env)' '$(test-program-prefix-after-env)' \
$(common-objpfx)stdlib/; \
$(evaluate-test)
+
+$(objpfx)tst-getenv-signal: $(shared-thread-library)
+$(objpfx)tst-getenv-thread: $(shared-thread-library)
+$(objpfx)tst-getenv-unsetenv: $(shared-thread-library)
diff -Nrup a/stdlib/getenv.c b/stdlib/getenv.c
--- a/stdlib/getenv.c 2025-02-26 21:42:43.962148893 -0500
+++ b/stdlib/getenv.c 2025-02-26 19:53:57.392341787 -0500
@@ -15,24 +15,144 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <stdlib.h>
+#include <atomic.h>
+#include <setenv.h>
#include <string.h>
#include <unistd.h>
+struct environ_array *__environ_array_list;
+environ_counter __environ_counter;
+
char *
getenv (const char *name)
{
- if (__environ == NULL || name[0] == '\0')
- return NULL;
-
- size_t len = strlen (name);
- for (char **ep = __environ; *ep != NULL; ++ep)
+ while (true)
{
- if (name[0] == (*ep)[0]
- && strncmp (name, *ep, len) == 0 && (*ep)[len] == '=')
- return *ep + len + 1;
- }
+ /* Used to deal with concurrent unsetenv. */
+ environ_counter start_counter = atomic_load_acquire (&__environ_counter);
+
+ /* We use relaxed MO for loading the string pointers because we
+ assume the strings themselves are immutable and that loads
+ through the string pointers carry a dependency. (This
+ depends on the the release MO store to __environ in
+ __add_to_environ.) Objects pointed to by pointers stored in
+ the __environ array are never modified or deallocated (except
+ perhaps if putenv is used, but then synchronization is the
+ responsibility of the applications). The backing store for
+ __environ is allocated zeroed. In summary, we can assume
+ that the pointers we observe are either valid or null, and
+ that only initialized string contents is visible. */
+ char **start_environ = atomic_load_relaxed (&__environ);
+ if (start_environ == NULL || name[0] == '\0')
+ return NULL;
+
+ size_t len = strlen (name);
+ for (char **ep = start_environ; ; ++ep)
+ {
+ char *entry = atomic_load_relaxed (ep);
+ if (entry == NULL)
+ break;
+
+ /* If there is a match, return that value. It was valid at
+ one point, so we can return it. */
+ if (name[0] == entry[0]
+ && strncmp (name, entry, len) == 0 && entry[len] == '=')
+ return entry + len + 1;
+ }
+
+ /* The variable was not found. This might be a false negative
+ because unsetenv has shuffled around entries. Check if it is
+ necessary to retry. */
+
+ /* See Hans Boehm, Can Seqlocks Get Along with Programming Language
+ Memory Models?, Section 4. This is necessary so that loads in
+ the loop above are not ordered past the counter check below. */
+ atomic_thread_fence_acquire ();
+
+ if (atomic_load_acquire (&__environ_counter) == start_counter)
+ /* If we reach this point and there was a concurrent
+ unsetenv call which removed the key we tried to find, the
+ NULL return value is valid. We can also try again, not
+ find the value, and then return NULL (assuming there are
+ no further concurrent unsetenv calls).
+
+ However, if getenv is called to find a value that is
+ present originally and not removed by any of the
+ concurrent unsetenv calls, we must not return NULL here.
+
+ If the counter did not change, there was at most one
+ write to the array in unsetenv while the scanning loop
+ above was running. This means that there are at most two
+ different versions of the array to consider. For the
+ sake of argument, we assume that each load can make an
+ independent choice which version to use. An arbitrary
+ number of unsetenv and setenv calls may have happened
+ since start of getenv. Lets write E[0], E[1], ... for
+ the original environment elements, a(0) < (1) < ... for a
+ sequence of increasing integers that are the indices of
+ the environment variables remaining after the removals, and
+ N[0], N[1], ... for the new variables added by setenv or
+ putenv. Then at the start of the last unsetenv call, the
+ environment contains
+
+ E[a(0)], E[a(1)], ..., N[0], N[1], ...
- return NULL;
+ (the N[0], N[1], .... are optional.) Let's assume that
+ we are looking for the value E[j]. Then one of the
+ a(i) == j (otherwise we may return NULL here because
+ of a unsetenv for the value we are looking for). In the
+ discussion below it will become clear that the N[k] do
+ not actually matter.
+
+ The two versions of array we can choose from differ only
+ in one element, say E[a(i)]. There are two cases:
+
+ Case (A): E[a(i)] is an element being removed by unsetenv
+ (the target of the first write). We can see the original
+ version:
+
+ ..., E[a(i-1)], E[a(i)], E[a(i+1)], ..., N[0], ...
+ -------
+ And the overwritten version:
+
+ ..., E[a(i-1)], E[a(i+1)], E[a(i+1)], ..., N[0], ...
+ ---------
+
+ (The valueE[a(i+1)] can be the terminating NULL.)
+ As discussed, we are not considering the removal of the
+ variable being searched for, so a(i) != j, and the
+ variable getenv is looking for is available in either
+ version, and we would have found it above.
+
+ Case (B): E[a(i)] is an element that has already been
+ moved forward and is now itself being overwritten with
+ its sucessor value E[a(i+1)]. The two versions of the
+ array look like this:
+
+ ..., E[a(i-1)], E[a(i)], E[a(i)], E[a(i+1)], ..., N[0], ...
+ -------
+ And with the overwrite in place:
+
+ ..., E[a(i-1)], E[a(i)], E[a(i+1)], E[a(i+1)], ..., N[0], ...
+ ---------
+
+ The key observation here is that even in the second
+ version with the overwrite present, the scanning loop
+ will still encounter the overwritten value E[a(i)] in the
+ previous array element. This means that as long as the
+ E[j] is still present among the initial E[a(...)] (as we
+ assumed because there is no concurrent unsetenv for
+ E[j]), we encounter it while scanning here in getenv.
+
+ In summary, if there was at most one write, a negative
+ result is a true negative, and we can return NULL. This
+ is different from the seqlock paper, which retries if
+ there was any write at all. It avoids the need for a
+ second, unwritten copy for async-signal-safety. */
+ return NULL;
+ /* If there was one more write, retry. This will never happen
+ in a signal handler that interrupted unsetenv because the
+ suspended unsetenv call cannot change the counter value. */
+ }
}
libc_hidden_def (getenv)
diff -Nrup a/stdlib/setenv.c b/stdlib/setenv.c
--- a/stdlib/setenv.c 2025-02-26 21:42:43.964148903 -0500
+++ b/stdlib/setenv.c 2025-02-26 19:53:57.392341787 -0500
@@ -19,6 +19,9 @@
# include <config.h>
#endif
+#include <assert.h>
+#include <setenv.h>
+
/* Pacify GCC; see the commentary about VALLEN below. This is needed
at least through GCC 4.9.2. Pacify GCC for the entire file, as
there seems to be no way to pacify GCC selectively, only for the
@@ -100,25 +103,51 @@ static void *known_values;
#endif
+/* Allocate a new environment array and put it o the
+ __environ_array_list. Returns NULL on memory allocation
+ failure. */
+static struct environ_array *
+__environ_new_array (size_t required_size)
+{
+ /* No backing array yet, or insufficient room. */
+ size_t new_size;
+ if (__environ_array_list == NULL
+ || __environ_array_list->allocated * 2 < required_size)
+ /* Add some unused space for future growth. */
+ new_size = required_size + 16;
+ else
+ new_size = __environ_array_list->allocated * 2;
+
+ size_t new_size_in_bytes;
+ if (__builtin_mul_overflow (new_size, sizeof (char *),
+ &new_size_in_bytes)
+ || __builtin_add_overflow (new_size_in_bytes,
+ offsetof (struct environ_array,
+ array),
+ &new_size_in_bytes))
+ {
+ __set_errno (ENOMEM);
+ return NULL;
+ }
-/* If this variable is not a null pointer we allocated the current
- environment. */
-static char **last_environ;
-
+ /* Zero-initialize everything, so that getenv can only
+ observe valid or null pointers. */
+ struct environ_array *target_array = calloc (1, new_size_in_bytes);
+ if (target_array == NULL)
+ return NULL;
+ target_array->allocated = new_size;
+ assert (new_size >= target_array->allocated);
+
+ /* Put it onto the list. */
+ target_array->next = __environ_array_list;
+ __environ_array_list = target_array;
+ return target_array;
+}
-/* This function is used by `setenv' and `putenv'. The difference between
- the two functions is that for the former must create a new string which
- is then placed in the environment, while the argument of `putenv'
- must be used directly. This is all complicated by the fact that we try
- to reuse values once generated for a `setenv' call since we can never
- free the strings. */
int
__add_to_environ (const char *name, const char *value, const char *combined,
int replace)
{
- char **ep;
- size_t size;
-
/* Compute lengths before locking, so that the critical section is
less of a performance bottleneck. VALLEN is needed only if
COMBINED is null (unfortunately GCC is not smart enough to deduce
@@ -133,45 +162,85 @@ __add_to_environ (const char *name, cons
LOCK;
/* We have to get the pointer now that we have the lock and not earlier
- since another thread might have created a new environment. */
- ep = __environ;
+ since another thread might have created a new environment. */
+ char **start_environ = atomic_load_relaxed (&__environ);
+ char **ep = start_environ;
+
+ /* This gets written to __environ in the end. */
+ char **result_environ = start_environ;
- size = 0;
+ /* Size of the environment if *ep == NULL. */
if (ep != NULL)
- {
- for (; *ep != NULL; ++ep)
- if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
- break;
- else
- ++size;
- }
+ for (; *ep != NULL; ++ep)
+ if (strncmp (*ep, name, namelen) == 0 && (*ep)[namelen] == '=')
+ break;
- if (ep == NULL || __builtin_expect (*ep == NULL, 1))
+ if (ep == NULL || __glibc_likely (*ep == NULL))
{
- char **new_environ;
-
- /* We allocated this space; we can extend it. Avoid using the raw
- reallocated pointer to avoid GCC -Wuse-after-free. */
- uintptr_t ip_last_environ = (uintptr_t)last_environ;
- new_environ = (char **) realloc (last_environ,
- (size + 2) * sizeof (char *));
- if (new_environ == NULL)
+ /* The scanning loop above reached the end of the environment.
+ Add a new string to it. */
+ replace = true;
+
+ /* + 2 for the new entry and the terminating NULL. */
+ size_t required_size = (ep - start_environ) + 2;
+ if (__environ_is_from_array_list (start_environ)
+ && required_size <= __environ_array_list->allocated)
+ /* The __environ array is ours, and we have room in it. We
+ can use ep as-is. Add a null terminator in case current
+ usage is less than previous usage. */
+ ep[1] = NULL;
+ else
{
- UNLOCK;
- return -1;
- }
+ /* We cannot use __environ as is and need to copy over the
+ __environ contents into an array managed via
+ __environ_array_list. */
+
+ struct environ_array *target_array;
+ if (__environ_array_list != NULL
+ && required_size <= __environ_array_list->allocated)
+ /* Existing array has enough room. Contents is copied below. */
+ target_array = __environ_array_list;
+ else
+ {
+ /* Allocate a new array. */
+ target_array = __environ_new_array (required_size);
+ if (target_array == NULL)
+ {
+ UNLOCK;
+ return -1;
+ }
+ }
- if ((uintptr_t)__environ != ip_last_environ)
- memcpy ((char *) new_environ, (char *) __environ,
- size * sizeof (char *));
-
- new_environ[size] = NULL;
- new_environ[size + 1] = NULL;
- ep = new_environ + size;
+ /* Copy over the __environ array contents. This forward
+ copy slides backwards part of the array if __environ
+ points into target_array->array. This happens if an
+ application makes an assignment like:
+
+ environ = &environ[1];
+
+ The forward copy avoids clobbering values that still
+ needing copying. This code handles the case
+ start_environ == ep == NULL, too. */
+ size_t i;
+ for (i = 0; start_environ + i < ep; ++i)
+ /* Regular store because unless there has been direct
+ manipulation of the environment, target_array is still
+ a private copy. */
+ target_array->array[i] = atomic_load_relaxed (start_environ + i);
+
+ /* This is the new place where we should add the element. */
+ ep = target_array->array + i;
+
+ /* Add the null terminator in case there was a pointer there
+ previously. */
+ ep[1] = NULL;
- last_environ = __environ = new_environ;
+ /* And __environ should be repointed to our array. */
+ result_environ = &target_array->array[0];
+ }
}
- if (*ep == NULL || replace)
+
+ if (replace || *ep == NULL)
{
char *np;
@@ -213,7 +282,12 @@ __add_to_environ (const char *name, cons
#endif
}
- *ep = np;
+ /* Use release MO so that loads are sufficient to observe the
+ pointer contents because the CPU carries the dependency for
+ us. This also acts as a thread fence, making getenv
+ async-signal-safe. */
+ atomic_store_release (ep, np);
+ atomic_store_release (&__environ, result_environ);
}
UNLOCK;
@@ -249,18 +323,40 @@ unsetenv (const char *name)
LOCK;
- ep = __environ;
+ ep = atomic_load_relaxed (&__environ);
if (ep != NULL)
- while (*ep != NULL)
+ while (true)
{
- if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
+ char *entry = atomic_load_relaxed (ep);
+ if (entry == NULL)
+ break;
+ if (strncmp (entry, name, len) == 0 && entry[len] == '=')
{
/* Found it. Remove this pointer by moving later ones back. */
char **dp = ep;
- do
- dp[0] = dp[1];
- while (*dp++);
+ while (true)
+ {
+ char *next_value = atomic_load_relaxed (dp + 1);
+ /* This store overwrites a value that has been
+ removed, or that has already been written to a
+ previous value. Release MO so that this store does
+ not get reordered before the counter update in the
+ previous loop iteration. */
+ atomic_store_release (dp, next_value);
+ /* Release store synchronizes with acquire loads in
+ getenv. Non-atomic update because there is just
+ one writer due to the lock.
+
+ See discussion of the counter check in getenv for
+ an explanation why this is sufficient synchronization. */
+ atomic_store_release (&__environ_counter,
+ atomic_load_relaxed (&__environ_counter)
+ + 1);
+ if (next_value == NULL)
+ break;
+ ++dp;
+ }
/* Continue the loop in case NAME appears again. */
}
else
@@ -279,17 +375,20 @@ int
clearenv (void)
{
LOCK;
-
- if (__environ == last_environ && __environ != NULL)
+ char **start_environ = atomic_load_relaxed (&__environ);
+ if (__environ_is_from_array_list (start_environ))
{
- /* We allocated this environment so we can free it. */
- free (__environ);
- last_environ = NULL;
+ /* Store null pointers to avoid strange effects when the array
+ is reused in setenv. */
+ for (char **ep = start_environ; *ep != NULL; ++ep)
+ atomic_store_relaxed (ep, NULL);
+ /* Update the counter similar to unsetenv, so that the writes in
+ setenv do not need to update the counter. */
+ atomic_store_release (&__environ_counter,
+ atomic_load_relaxed (&__environ_counter) + 1);
}
- /* Clear the environment pointer removes the whole environment. */
- __environ = NULL;
-
+ atomic_store_relaxed (&__environ, NULL);
UNLOCK;
return 0;
@@ -300,6 +399,14 @@ libc_freeres_fn (free_mem)
/* Remove all traces. */
clearenv ();
+ /* Clear all backing arrays. */
+ while (__environ_array_list != NULL)
+ {
+ void *ptr = __environ_array_list;
+ __environ_array_list = __environ_array_list->next;
+ free (ptr);
+ }
+
/* Now remove the search tree. */
__tdestroy (known_values, free);
known_values = NULL;
diff -Nrup a/stdlib/setenv.h b/stdlib/setenv.h
--- a/stdlib/setenv.h 1969-12-31 19:00:00.000000000 -0500
+++ b/stdlib/setenv.h 2025-02-26 19:53:57.392341787 -0500
@@ -0,0 +1,73 @@
+/* Common declarations for the setenv/getenv family of functions.
+ 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/>. */
+
+#ifndef _SETENV_H
+#define _SETENV_H
+
+#include <atomic.h>
+#include <stdbool.h>
+
+/* We use an exponential sizing policy for environment arrays. The
+ arrays are not deallocating during the lifetime of the process.
+ This adds between one and two additional pointers per active
+ environemnt entry, on top of what is used by setenv to keep track
+ of environment values used before. */
+struct environ_array
+{
+ struct environ_array *next; /* Previously used environment array. */
+ size_t allocated; /* Number of allocated array elments. */
+ char *array[]; /* The actual environment array. */
+};
+
+/* After initialization, and until the user resets environ (perhaps by
+ calling clearenv), &__environ[0] == &environ_array_list->array[0]. */
+extern struct environ_array *__environ_array_list attribute_hidden;
+
+/* Returns true if EP (which should be an __environ value) is a
+ pointer managed by setenv. */
+static inline bool
+__environ_is_from_array_list (char **ep)
+{
+ struct environ_array *eal = atomic_load_relaxed (&__environ_array_list);
+ return eal != NULL && &eal->array[0] == ep;
+}
+
+/* Counter for detecting concurrent modification in unsetenv.
+ Ideally, this should be a 64-bit counter that cannot wrap around,
+ but given that counter wrapround is probably impossible to hit
+ (2**32 operations in unsetenv concurrently with getenv), using
+ <atomic_wide_counter.h> seems unnecessary. */
+#if __HAVE_64B_ATOMICS
+typedef uint64_t environ_counter;
+#else
+typedef uint32_t environ_counter;
+#endif
+
+/* Updated by unsetenv to detect multiple overwrites in getenv. */
+extern environ_counter __environ_counter attribute_hidden;
+
+/* This function is used by `setenv' and `putenv'. The difference between
+ the two functions is that for the former must create a new string which
+ is then placed in the environment, while the argument of `putenv'
+ must be used directly. This is all complicated by the fact that we try
+ to reuse values once generated for a `setenv' call since we can never
+ free the strings. */
+int __add_to_environ (const char *name, const char *value,
+ const char *combines, int replace) attribute_hidden;
+
+#endif /* _SETENV_H */
diff -Nrup a/stdlib/tst-environ.c b/stdlib/tst-environ.c
--- a/stdlib/tst-environ.c 2021-08-01 21:33:43.000000000 -0400
+++ b/stdlib/tst-environ.c 2025-02-26 19:53:57.393341792 -0500
@@ -20,6 +20,7 @@
#include <stdlib.h>
#include <string.h>
#include <libc-diag.h>
+#include <support/check.h>
#define VAR "FOOBAR"
@@ -50,11 +51,7 @@ do_test (void)
/* Getting this value should now be possible. */
valp = getenv (VAR);
- if (valp == NULL || strcmp (valp, "one") != 0)
- {
- puts ("getenv #2 failed");
- result = 1;
- }
+ TEST_COMPARE_STRING (valp, "one");
/* Try to replace without the replace flag set. This should fail. */
if (setenv (VAR, "two", 0) != 0)
@@ -65,11 +62,7 @@ do_test (void)
/* The value shouldn't have changed. */
valp = getenv (VAR);
- if (valp == NULL || strcmp (valp, "one") != 0)
- {
- puts ("getenv #3 failed");
- result = 1;
- }
+ TEST_COMPARE_STRING (valp, "one");
/* Now replace the value using putenv. */
if (putenv (putenv_val) != 0)
diff -Nrup a/stdlib/tst-getenv-signal.c b/stdlib/tst-getenv-signal.c
--- a/stdlib/tst-getenv-signal.c 1969-12-31 19:00:00.000000000 -0500
+++ b/stdlib/tst-getenv-signal.c 2025-02-26 19:53:57.393341792 -0500
@@ -0,0 +1,94 @@
+/* Test getenv from a signal handler interrupting environment updates.
+ 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 <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xthread.h>
+#include <support/xsignal.h>
+
+/* Set to false by the main thread after doing all the setenv
+ calls. */
+static bool running = true;
+
+/* Used to synchronize the start of signal sending. */
+static pthread_barrier_t barrier;
+
+/* Identity of the main thread. */
+static pthread_t main_thread;
+
+/* Send SIGUSR1 signals to main_thread. */
+static void *
+signal_thread (void *ignored)
+{
+ xpthread_barrier_wait (&barrier);
+ while (__atomic_load_n (&running, __ATOMIC_RELAXED))
+ xpthread_kill (main_thread, SIGUSR1);
+ return NULL;
+}
+
+/* Call getenv from a signal handler. */
+static void
+signal_handler (int signo)
+{
+ TEST_COMPARE_STRING (getenv ("unset_variable"), NULL);
+ char *value = getenv ("set_variable");
+ TEST_VERIFY (strncmp (value, "value", strlen ("value")) == 0);
+}
+
+static int
+do_test (void)
+{
+ /* Added to the environment using putenv. */
+ char *variables[30];
+ for (int i = 0; i < array_length (variables); ++i)
+ variables[i] = xasprintf ("v%d=%d", i, i);
+
+ xsignal (SIGUSR1, signal_handler);
+ TEST_COMPARE (setenv ("set_variable", "value", 1), 0);
+ xraise (SIGUSR1);
+ main_thread = pthread_self ();
+ xpthread_barrier_init (&barrier, NULL, 2);
+ pthread_t thr = xpthread_create (NULL, signal_thread, NULL);
+ xpthread_barrier_wait (&barrier);
+
+ for (int i = 0; i < array_length (variables); ++i)
+ {
+ char buf[30];
+ TEST_COMPARE (setenv ("temporary_variable", "1", 1), 0);
+ snprintf (buf, sizeof (buf), "V%d", i);
+ TEST_COMPARE (setenv (buf, buf + 1, 1), 0);
+ TEST_COMPARE (putenv (variables[i]), 0);
+ snprintf (buf, sizeof (buf), "value%d", i);
+ TEST_COMPARE (setenv ("set_variable", buf, 1), 0);
+ TEST_COMPARE (unsetenv ("temporary_variable"), 0);
+ }
+
+ __atomic_store_n (&running, false, __ATOMIC_RELAXED);
+ xpthread_join (thr);
+ xpthread_barrier_destroy (&barrier);
+
+ for (int i = 0; i < array_length (variables); ++i)
+ free (variables[i]);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff -Nrup a/stdlib/tst-getenv-thread.c b/stdlib/tst-getenv-thread.c
--- a/stdlib/tst-getenv-thread.c 1969-12-31 19:00:00.000000000 -0500
+++ b/stdlib/tst-getenv-thread.c 2025-02-26 19:53:57.393341792 -0500
@@ -0,0 +1,62 @@
+/* Test getenv with concurrent setenv.
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/xthread.h>
+
+/* Set to false by the main thread after doing all the setenv
+ calls. */
+static bool running = true;
+
+/* Used to synchronize the start of the getenv thread. */
+static pthread_barrier_t barrier;
+
+/* Invoke getenv for a nonexisting environment variable in a loop.
+ This checks that concurrent setenv does not invalidate the
+ environment array while getenv reads it. */
+static void *
+getenv_thread (void *ignored)
+{
+ xpthread_barrier_wait (&barrier);
+ while (__atomic_load_n (&running, __ATOMIC_RELAXED))
+ TEST_VERIFY (getenv ("unset_variable") == NULL);
+ return NULL;
+}
+
+static int
+do_test (void)
+{
+ xpthread_barrier_init (&barrier, NULL, 2);
+ pthread_t thr = xpthread_create (NULL, getenv_thread, NULL);
+ xpthread_barrier_wait (&barrier);
+ for (int i = 0; i < 1000; ++i)
+ {
+ char buf[30];
+ snprintf (buf, sizeof (buf), "V%d", i);
+ TEST_COMPARE (setenv (buf, buf + 1, 1), 0);
+ }
+ __atomic_store_n (&running, false, __ATOMIC_RELAXED);
+ xpthread_join (thr);
+ xpthread_barrier_destroy (&barrier);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff -Nrup a/stdlib/tst-getenv-unsetenv.c b/stdlib/tst-getenv-unsetenv.c
--- a/stdlib/tst-getenv-unsetenv.c 1969-12-31 19:00:00.000000000 -0500
+++ b/stdlib/tst-getenv-unsetenv.c 2025-02-26 19:53:57.393341792 -0500
@@ -0,0 +1,75 @@
+/* Test getenv with concurrent unsetenv.
+ 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 <stdlib.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xthread.h>
+
+/* Used to synchronize the start of each test iteration. */
+static pthread_barrier_t barrier;
+
+/* Number of iterations. */
+enum { iterations = 10000 };
+
+/* Check that even with concurrent unsetenv, a variable that is known
+ to be there is found. */
+static void *
+getenv_thread (void *ignored)
+{
+ for (int i = 0; i < iterations; ++i)
+ {
+ xpthread_barrier_wait (&barrier);
+ TEST_COMPARE_STRING (getenv ("variable"), "value");
+ xpthread_barrier_wait (&barrier);
+ }
+ return NULL;
+}
+
+static int
+do_test (void)
+{
+ xpthread_barrier_init (&barrier, NULL, 2);
+ pthread_t thr = xpthread_create (NULL, getenv_thread, NULL);
+
+ char *variables[50];
+ for (int i = 0; i < array_length (variables); ++i)
+ variables[i] = xasprintf ("V%d", i);
+
+ for (int i = 0; i < iterations; ++i)
+ {
+ clearenv ();
+ for (int j = 0; j < array_length (variables); ++j)
+ TEST_COMPARE (setenv (variables[j], variables[j] + 1, 1), 0);
+ TEST_COMPARE (setenv ("variable", "value", 1), 0);
+ xpthread_barrier_wait (&barrier);
+ /* Test runs. */
+ for (int j = 0; j < array_length (variables); ++j)
+ TEST_COMPARE (unsetenv (variables[j]), 0);
+ xpthread_barrier_wait (&barrier);
+ }
+ xpthread_join (thr);
+ xpthread_barrier_destroy (&barrier);
+ for (int i = 0; i < array_length (variables); ++i)
+ free (variables[i]);
+ return 0;
+}
+
+#include <support/test-driver.c>

View File

@ -0,0 +1,56 @@
commit 68ee0f704cb81e9ad0a78c644a83e1e9cd2ee578
Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
Date: Tue Jan 21 16:11:06 2025 -0500
Fix underallocation of abort_msg_s struct (CVE-2025-0395)
Include the space needed to store the length of the message itself, in
addition to the message string. This resolves BZ #32582.
Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/assert/assert.c b/assert/assert.c
index 989126c7e5b6b265..d14bb76b1fb43f57 100644
--- a/assert/assert.c
+++ b/assert/assert.c
@@ -18,6 +18,7 @@
#include <assert.h>
#include <atomic.h>
#include <ldsodefs.h>
+#include <libc-pointer-arith.h>
#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
@@ -64,7 +65,8 @@ __assert_fail_base (const char *fmt, const char *assertion, const char *file,
(void) __fxprintf (NULL, "%s", str);
(void) fflush (stderr);
- total = (total + 1 + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1);
+ total = ALIGN_UP (total + sizeof (struct abort_msg_s) + 1,
+ GLRO(dl_pagesize));
struct abort_msg_s *buf = __mmap (NULL, total, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
if (__glibc_likely (buf != MAP_FAILED))
diff --git a/sysdeps/posix/libc_fatal.c b/sysdeps/posix/libc_fatal.c
index 1feacfbeba765035..7929f1601d4a89c9 100644
--- a/sysdeps/posix/libc_fatal.c
+++ b/sysdeps/posix/libc_fatal.c
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <ldsodefs.h>
+#include <libc-pointer-arith.h>
#include <paths.h>
#include <stdarg.h>
#include <stdbool.h>
@@ -123,7 +124,8 @@ __libc_message (const char *fmt, ...)
WRITEV_FOR_FATAL (fd, iov, nlist, total);
- total = (total + 1 + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1);
+ total = ALIGN_UP (total + sizeof (struct abort_msg_s) + 1,
+ GLRO(dl_pagesize));
struct abort_msg_s *buf = __mmap (NULL, total,
PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);

View File

@ -0,0 +1,54 @@
commit ebd928224a138d4560dc0be3ef162162d62a9e43
Author: Carlos O'Donell <carlos@redhat.com>
Date: Thu May 18 12:56:45 2023 -0400
assert: Reformat Makefile.
Reflow all long lines adding comment terminators.
Sort all reflowed text using scripts/sort-makefile-lines.py.
No code generation changes observed in binary artifacts.
No regressions on x86_64 and i686.
diff --git a/assert/Makefile b/assert/Makefile
index 2bc9e2214e3e9a8b..24a9bdb96306ca08 100644
--- a/assert/Makefile
+++ b/assert/Makefile
@@ -22,7 +22,9 @@ subdir := assert
include ../Makeconfig
-headers := assert.h
+headers := \
+ assert.h
+ # headers
routines := \
__assert \
@@ -30,7 +32,13 @@ routines := \
assert \
assert-perr \
# routines
-tests := test-assert test-assert-perr tst-assert-c++ tst-assert-g++
+
+tests := \
+ test-assert \
+ test-assert-perr \
+ tst-assert-c++ \
+ tst-assert-g++ \
+ # tests
ifeq ($(have-cxx-thread_local),yes)
CFLAGS-tst-assert-c++.o = -std=c++11
@@ -38,7 +46,10 @@ LDLIBS-tst-assert-c++ = -lstdc++
CFLAGS-tst-assert-g++.o = -std=gnu++11
LDLIBS-tst-assert-g++ = -lstdc++
else
-tests-unsupported += tst-assert-c++ tst-assert-g++
+tests-unsupported += \
+ tst-assert-c++ \
+ tst-assert-g++ \
+ # tests-unsupported
endif
include ../Rules

View File

@ -0,0 +1,124 @@
commit cdb9ba84191ce72e86346fb8b1d906e7cd930ea2
Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
Date: Fri Jan 31 12:16:30 2025 -0500
assert: Add test for CVE-2025-0395
Use the __progname symbol to override the program name to induce the
failure that CVE-2025-0395 describes.
This is related to BZ #32582
Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/assert/Makefile b/assert/Makefile
index 24a9bdb96306ca08..a2048993025ec4dc 100644
--- a/assert/Makefile
+++ b/assert/Makefile
@@ -38,6 +38,7 @@ tests := \
test-assert-perr \
tst-assert-c++ \
tst-assert-g++ \
+ tst-assert-sa-2025-0001 \
# tests
ifeq ($(have-cxx-thread_local),yes)
diff --git a/assert/tst-assert-sa-2025-0001.c b/assert/tst-assert-sa-2025-0001.c
new file mode 100644
index 0000000000000000..102cb0078dafa9c1
--- /dev/null
+++ b/assert/tst-assert-sa-2025-0001.c
@@ -0,0 +1,92 @@
+/* Test for CVE-2025-0395.
+ Copyright The GNU Toolchain Authors.
+ 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/>. */
+
+/* Test that a large enough __progname does not result in a buffer overflow
+ when printing an assertion failure. This was CVE-2025-0395. */
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+extern const char *__progname;
+
+int
+do_test (int argc, char **argv)
+{
+
+ support_need_proc ("Reads /proc/self/maps to add guards to writable maps.");
+ ignore_stderr ();
+
+ /* XXX assumes that the assert is on a 2 digit line number. */
+ const char *prompt = ": %s:99: do_test: Assertion `argc < 1' failed.\n";
+
+ int ret = fprintf (stderr, prompt, __FILE__);
+ if (ret < 0)
+ FAIL_EXIT1 ("fprintf failed: %m\n");
+
+ size_t pagesize = getpagesize ();
+ size_t namesize = pagesize - 1 - ret;
+
+ /* Alter the progname so that the assert message fills the entire page. */
+ char progname[namesize];
+ memset (progname, 'A', namesize - 1);
+ progname[namesize - 1] = '\0';
+ __progname = progname;
+
+ FILE *f = xfopen ("/proc/self/maps", "r");
+ char *line = NULL;
+ size_t len = 0;
+ uintptr_t prev_to = 0;
+
+ /* Pad the beginning of every writable mapping with a PROT_NONE map. This
+ ensures that the mmap in the assert_fail path never ends up below a
+ writable map and will terminate immediately in case of a buffer
+ overflow. */
+ while (xgetline (&line, &len, f))
+ {
+ uintptr_t from, to;
+ char perm[4];
+
+ sscanf (line, "%" SCNxPTR "-%" SCNxPTR " %c%c%c%c ",
+ &from, &to,
+ &perm[0], &perm[1], &perm[2], &perm[3]);
+
+ bool writable = (memchr (perm, 'w', 4) != NULL);
+
+ if (prev_to != 0 && from - prev_to > pagesize && writable)
+ xmmap ((void *) from - pagesize, pagesize, PROT_NONE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0);
+
+ prev_to = to;
+ }
+
+ xfclose (f);
+
+ assert (argc < 1);
+ return 0;
+}
+
+#define EXPECTED_SIGNAL SIGABRT
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>

View File

@ -0,0 +1,45 @@
commit 7a76f218677d149d8b7875b336722108239f7ee9
Author: Florian Weimer <fweimer@redhat.com>
Date: Fri Mar 15 19:08:24 2024 +0100
linux: Use rseq area unconditionally in sched_getcpu (bug 31479)
Originally, nptl/descr.h included <sys/rseq.h>, but we removed that
in commit 2c6b4b272e6b4d07303af25709051c3e96288f2d ("nptl:
Unconditionally use a 32-byte rseq area"). After that, it was
not ensured that the RSEQ_SIG macro was defined during sched_getcpu.c
compilation that provided a definition. This commit always checks
the rseq area for CPU number information before using the other
approaches.
This adds an unnecessary (but well-predictable) branch on
architectures which do not define RSEQ_SIG, but its cost is small
compared to the system call. Most architectures that have vDSO
acceleration for getcpu also have rseq support.
Fixes: 2c6b4b272e6b4d07303af25709051c3e96288f2d
Fixes: 1d350aa06091211863e41169729cee1bca39f72f
Reviewed-by: Arjun Shankar <arjun@redhat.com>
diff --git a/sysdeps/unix/sysv/linux/sched_getcpu.c b/sysdeps/unix/sysv/linux/sched_getcpu.c
index 6f78edaea1495342..a33e30a1cb8e161b 100644
--- a/sysdeps/unix/sysv/linux/sched_getcpu.c
+++ b/sysdeps/unix/sysv/linux/sched_getcpu.c
@@ -33,17 +33,9 @@ vsyscall_sched_getcpu (void)
return r == -1 ? r : cpu;
}
-#ifdef RSEQ_SIG
int
sched_getcpu (void)
{
int cpu_id = THREAD_GETMEM_VOLATILE (THREAD_SELF, rseq_area.cpu_id);
return __glibc_likely (cpu_id >= 0) ? cpu_id : vsyscall_sched_getcpu ();
}
-#else /* RSEQ_SIG */
-int
-sched_getcpu (void)
-{
- return vsyscall_sched_getcpu ();
-}
-#endif /* RSEQ_SIG */

View File

@ -0,0 +1,161 @@
commit f3c6c190388bb445568cfbf190a0942fc3c28553
Author: DJ Delorie <dj@redhat.com>
Date: Tue Oct 5 14:52:05 2021 -0400
Remove unreliable parts of rt/tst-cpuclock2
This is a follow-up to the tst-cpuclock1.c change here:
9a29f1a2ae3d4bb253ee368e0d71db0ca9494120
This test, like tst-cpuclock1, may fail on heavily loaded VM
servers (and has occasionally failed on the 32bit trybot),
so tests that rely on "wall time" have been removed.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/rt/tst-cpuclock2.c b/rt/tst-cpuclock2.c
index eebc3609d0aa88da..32a1b75c2f3f5d18 100644
--- a/rt/tst-cpuclock2.c
+++ b/rt/tst-cpuclock2.c
@@ -62,22 +62,9 @@ chew_cpu (void *arg)
return NULL;
}
-static unsigned long long int
-tsdiff (const struct timespec *before, const struct timespec *after)
-{
- struct timespec diff = { .tv_sec = after->tv_sec - before->tv_sec,
- .tv_nsec = after->tv_nsec - before->tv_nsec };
- while (diff.tv_nsec < 0)
- {
- --diff.tv_sec;
- diff.tv_nsec += 1000000000;
- }
- return diff.tv_sec * 1000000000ULL + diff.tv_nsec;
-}
-
-static unsigned long long int
+static void
test_nanosleep (clockid_t clock, const char *which,
- const struct timespec *before, int *bad)
+ int *bad)
{
const struct timespec sleeptime = { .tv_nsec = 100000000 };
int e = clock_nanosleep (clock, 0, &sleeptime, NULL);
@@ -85,13 +72,13 @@ test_nanosleep (clockid_t clock, const char *which,
{
printf ("clock_nanosleep not supported for %s CPU clock: %s\n",
which, strerror (e));
- return 0;
+ return;
}
if (e != 0)
{
printf ("clock_nanosleep on %s CPU clock: %s\n", which, strerror (e));
*bad = 1;
- return 0;
+ return;
}
struct timespec after;
@@ -100,16 +87,7 @@ test_nanosleep (clockid_t clock, const char *which,
printf ("clock_gettime on %s CPU clock %lx => %s\n",
which, (unsigned long int) clock, strerror (errno));
*bad = 1;
- return 0;
- }
-
- unsigned long long int diff = tsdiff (before, &after);
- if (diff < sleeptime.tv_nsec || diff > sleeptime.tv_nsec * 2)
- {
- printf ("clock_nanosleep on %s slept %llu (outside reasonable range)\n",
- which, diff);
- *bad = 1;
- return diff;
+ return;
}
struct timespec sleeptimeabs = sleeptime;
@@ -126,7 +104,7 @@ test_nanosleep (clockid_t clock, const char *which,
printf ("absolute clock_nanosleep on %s CPU clock: %s\n",
which, strerror (e));
*bad = 1;
- return diff;
+ return;
}
struct timespec afterabs;
@@ -135,28 +113,10 @@ test_nanosleep (clockid_t clock, const char *which,
printf ("clock_gettime on %s CPU clock %lx => %s\n",
which, (unsigned long int) clock, strerror (errno));
*bad = 1;
- return diff;
- }
-
- unsigned long long int sleepdiff = tsdiff (&sleeptimeabs, &afterabs);
- if (sleepdiff > sleeptime.tv_nsec)
- {
- printf ("\
-absolute clock_nanosleep on %s %llu past target (outside reasonable range)\n",
- which, sleepdiff);
- *bad = 1;
+ return;
}
- unsigned long long int diffabs = tsdiff (&after, &afterabs);
- if (diffabs < sleeptime.tv_nsec || diffabs > sleeptime.tv_nsec * 2)
- {
- printf ("\
-absolute clock_nanosleep on %s slept %llu (outside reasonable range)\n",
- which, diffabs);
- *bad = 1;
- }
-
- return diff + diffabs;
+ return;
}
@@ -290,37 +250,12 @@ do_test (void)
printf ("self thread after sleep => %ju.%.9ju\n",
(uintmax_t) me_after.tv_sec, (uintmax_t) me_after.tv_nsec);
- unsigned long long int th_diff = tsdiff (&before, &after);
- unsigned long long int pdiff = tsdiff (&process_before, &process_after);
- unsigned long long int my_diff = tsdiff (&me_before, &me_after);
-
- if (th_diff < 100000000 || th_diff > 600000000)
- {
- printf ("live thread before - after %llu outside reasonable range\n",
- th_diff);
- result = 1;
- }
-
- if (my_diff > 100000000)
- {
- printf ("self thread before - after %llu outside reasonable range\n",
- my_diff);
- result = 1;
- }
-
- if (pdiff < th_diff)
- {
- printf ("process before - after %llu outside reasonable range (%llu)\n",
- pdiff, th_diff);
- result = 1;
- }
-
- process_after.tv_nsec += test_nanosleep (th_clock, "live thread",
- &after, &result);
- process_after.tv_nsec += test_nanosleep (process_clock, "process",
- &process_after, &result);
+ test_nanosleep (th_clock, "live thread",
+ &result);
+ test_nanosleep (process_clock, "process",
+ &result);
test_nanosleep (CLOCK_PROCESS_CPUTIME_ID,
- "PROCESS_CPUTIME_ID", &process_after, &result);
+ "PROCESS_CPUTIME_ID", &result);
pthread_cancel (th);

View File

@ -157,7 +157,7 @@ end \
Summary: The GNU libc libraries
Name: glibc
Version: %{glibcversion}
Release: 125%{?dist}.3.alma.1
Release: 125%{?dist}.8.alma.1
# In general, GPLv2+ is used by programs, LGPLv2+ is used for
# libraries.
@ -882,6 +882,20 @@ Patch643: glibc-RHEL-78939-7.patch
Patch644: glibc-RHEL-78939-8.patch
Patch645: glibc-RHEL-78939-9.patch
Patch646: glibc-RHEL-78939-10.patch
Patch647: glibc-RHEL-70707-1.patch
Patch648: glibc-RHEL-70707-2.patch
Patch649: glibc-RHEL-70707-3.patch
Patch650: glibc-RHEL-70707-4.patch
Patch651: glibc-RHEL-84324.patch
Patch652: glibc-RHEL-83294-1.patch
Patch653: glibc-RHEL-83294-2.patch
Patch654: glibc-RHEL-83294-3.patch
Patch655: glibc-RHEL-65285-1.patch
Patch656: glibc-RHEL-65285-2.patch
Patch657: glibc-RHEL-65285-3.patch
Patch658: glibc-RHEL-65285-4.patch
Patch659: glibc-RHEL-65285-5.patch
Patch660: glibc-RHEL-83524.patch
# AlmaLinux Patch
Patch1000: glibc-upstream-2.34-bz-32470.patch
@ -3044,9 +3058,24 @@ update_gconv_modules_cache ()
%endif
%changelog
* Mon Mar 10 2025 Koichiro Iwao <meta@almalinux.org> - 2.34-125.3.alma.1
* Mon Apr 28 2025 Koichiro Iwao <meta@almalinux.org> - 2.34-125.8.alma.1
- Apply patch from upstream BZ #32470
* Thu Apr 3 2025 Florian Weimer <fweimer@redhat.com> - 2.34-125.8
- Re-activate rseq acceleration for sched_getcpu (RHEL-83524)
* Thu Apr 3 2025 Florian Weimer <fweimer@redhat.com> - 2.34-125.7
- Adjust __rseq_size behavior to upstream (RHEL-65285)
* Fri Mar 28 2025 Arjun Shankar <arjun@redhat.com> - 2.34-125.6
- CVE-2025-0395: Fix a buffer overflow in assert (RHEL-83294)
* Fri Mar 28 2025 Arjun Shankar <arjun@redhat.com> - 2.34-125.5
- Make test tst-cpuclock2 run more reliably (RHEL-84324)
* Wed Feb 26 2025 Patsy Griffin <patsy@redhat.com> - 2.34-125.4
- stdlib: Make getenv thread-safe in more cases (RHEL-70707)
* Thu Feb 13 2025 Carlos O'Donell <carlos@redhat.com> - 2.34-125.3
- Fix missed wakeup in POSIX thread condition variables (RHEL-78939)