diff --git a/SOURCES/glibc-RHEL-65285-1.patch b/SOURCES/glibc-RHEL-65285-1.patch new file mode 100644 index 0000000..e596d78 --- /dev/null +++ b/SOURCES/glibc-RHEL-65285-1.patch @@ -0,0 +1,29 @@ +commit 8754a4133e154ca853e6765a3fe5c7a904c77626 +Author: Joseph Myers +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 diff --git a/SOURCES/glibc-RHEL-65285-2.patch b/SOURCES/glibc-RHEL-65285-2.patch new file mode 100644 index 0000000..ad91f7a --- /dev/null +++ b/SOURCES/glibc-RHEL-65285-2.patch @@ -0,0 +1,152 @@ +commit 2b92982e2369d292560793bee8e730f695f48ff3 +Author: Michael Jeanson +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 + Reviewed-by: Mathieu Desnoyers + Reviewed-by: Florian Weimer + +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 ++ . */ ++ ++#include ++ ++#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 + } + diff --git a/SOURCES/glibc-RHEL-65285-3.patch b/SOURCES/glibc-RHEL-65285-3.patch new file mode 100644 index 0000000..56cc81f --- /dev/null +++ b/SOURCES/glibc-RHEL-65285-3.patch @@ -0,0 +1,136 @@ +commit 2e456ccf0c34a056e3ccafac4a0c7effef14d918 +Author: Florian Weimer +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 + Reviewed-by: Mathieu Desnoyers + +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 + #include + ++/* 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 + # include + # include ++# include + # include + # include + # 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 */ diff --git a/SOURCES/glibc-RHEL-65285-4.patch b/SOURCES/glibc-RHEL-65285-4.patch new file mode 100644 index 0000000..55bd8f0 --- /dev/null +++ b/SOURCES/glibc-RHEL-65285-4.patch @@ -0,0 +1,54 @@ +commit 97f60abd25628425971f07e9b0e7f8eec0741235 +Author: Michael Jeanson +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 + Reviewed-by: Florian Weimer + +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; + } diff --git a/SOURCES/glibc-RHEL-65285-5.patch b/SOURCES/glibc-RHEL-65285-5.patch new file mode 100644 index 0000000..8265f04 --- /dev/null +++ b/SOURCES/glibc-RHEL-65285-5.patch @@ -0,0 +1,28 @@ +commit d9f40387d3305d97e30a8cf8724218c42a63680a +Author: Michael Jeanson +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 + Reviewed-by: Mathieu Desnoyers + +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); + diff --git a/SOURCES/glibc-RHEL-70707-1.patch b/SOURCES/glibc-RHEL-70707-1.patch new file mode 100644 index 0000000..6e647d1 --- /dev/null +++ b/SOURCES/glibc-RHEL-70707-1.patch @@ -0,0 +1,32 @@ +commit 4f20a1dc5242fb4bb8763e0451df898fa48e740c +Author: Martin Sebor +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 + +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 *)); + diff --git a/SOURCES/glibc-RHEL-70707-2.patch b/SOURCES/glibc-RHEL-70707-2.patch new file mode 100644 index 0000000..b98de8f --- /dev/null +++ b/SOURCES/glibc-RHEL-70707-2.patch @@ -0,0 +1,98 @@ +commit a6ccce23afc2a09a17ac2a86a2b726b58df609df +Author: Adhemerval Zanella +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 + +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 + . */ + +-#include +-#include +-#include + #include + #include + #include + +- +-/* 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; diff --git a/SOURCES/glibc-RHEL-70707-3.patch b/SOURCES/glibc-RHEL-70707-3.patch new file mode 100644 index 0000000..7ecd7de --- /dev/null +++ b/SOURCES/glibc-RHEL-70707-3.patch @@ -0,0 +1,75 @@ +commit 9401024e5e6be0e1c3870e185daae865cd4501f4 +Author: Joe Simmons-Talbott +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 + +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 + } + diff --git a/SOURCES/glibc-RHEL-70707-4.patch b/SOURCES/glibc-RHEL-70707-4.patch new file mode 100644 index 0000000..db4fe2a --- /dev/null +++ b/SOURCES/glibc-RHEL-70707-4.patch @@ -0,0 +1,882 @@ +commit 7a61e7f557a97ab597d6fca5e2d1f13f65685c61 +Author: Florian Weimer +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 + +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 + . */ + +-#include ++#include ++#include + #include + #include + ++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 + #endif + ++#include ++#include ++ + /* 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 ++ . */ ++ ++#ifndef _SETENV_H ++#define _SETENV_H ++ ++#include ++#include ++ ++/* 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 ++ 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 + #include + #include ++#include + + #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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 +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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 +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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 diff --git a/SOURCES/glibc-RHEL-83294-1.patch b/SOURCES/glibc-RHEL-83294-1.patch new file mode 100644 index 0000000..c769d38 --- /dev/null +++ b/SOURCES/glibc-RHEL-83294-1.patch @@ -0,0 +1,56 @@ +commit 68ee0f704cb81e9ad0a78c644a83e1e9cd2ee578 +Author: Siddhesh Poyarekar +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 + Reviewed: Adhemerval Zanella + +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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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); diff --git a/SOURCES/glibc-RHEL-83294-2.patch b/SOURCES/glibc-RHEL-83294-2.patch new file mode 100644 index 0000000..0a02cd5 --- /dev/null +++ b/SOURCES/glibc-RHEL-83294-2.patch @@ -0,0 +1,54 @@ +commit ebd928224a138d4560dc0be3ef162162d62a9e43 +Author: Carlos O'Donell +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 diff --git a/SOURCES/glibc-RHEL-83294-3.patch b/SOURCES/glibc-RHEL-83294-3.patch new file mode 100644 index 0000000..72fa417 --- /dev/null +++ b/SOURCES/glibc-RHEL-83294-3.patch @@ -0,0 +1,124 @@ +commit cdb9ba84191ce72e86346fb8b1d906e7cd930ea2 +Author: Siddhesh Poyarekar +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 + Reviewed-by: Adhemerval Zanella + +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 ++ . */ ++ ++/* Test that a large enough __progname does not result in a buffer overflow ++ when printing an assertion failure. This was CVE-2025-0395. */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 diff --git a/SOURCES/glibc-RHEL-83524.patch b/SOURCES/glibc-RHEL-83524.patch new file mode 100644 index 0000000..ea21088 --- /dev/null +++ b/SOURCES/glibc-RHEL-83524.patch @@ -0,0 +1,45 @@ +commit 7a76f218677d149d8b7875b336722108239f7ee9 +Author: Florian Weimer +Date: Fri Mar 15 19:08:24 2024 +0100 + + linux: Use rseq area unconditionally in sched_getcpu (bug 31479) + + Originally, nptl/descr.h included , 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 + +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 */ diff --git a/SOURCES/glibc-RHEL-84324.patch b/SOURCES/glibc-RHEL-84324.patch new file mode 100644 index 0000000..d2da81f --- /dev/null +++ b/SOURCES/glibc-RHEL-84324.patch @@ -0,0 +1,161 @@ +commit f3c6c190388bb445568cfbf190a0942fc3c28553 +Author: DJ Delorie +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 + +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); + diff --git a/SPECS/glibc.spec b/SPECS/glibc.spec index 76474d4..2b477dd 100644 --- a/SPECS/glibc.spec +++ b/SPECS/glibc.spec @@ -157,7 +157,7 @@ end \ Summary: The GNU libc libraries Name: glibc Version: %{glibcversion} -Release: 125%{?dist}.3 +Release: 125%{?dist}.8 # 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 ############################################################################## # Continued list of core "glibc" package information: @@ -3041,6 +3055,21 @@ update_gconv_modules_cache () %endif %changelog +* Thu Apr 3 2025 Florian Weimer - 2.34-125.8 +- Re-activate rseq acceleration for sched_getcpu (RHEL-83524) + +* Thu Apr 3 2025 Florian Weimer - 2.34-125.7 +- Adjust __rseq_size behavior to upstream (RHEL-65285) + +* Fri Mar 28 2025 Arjun Shankar - 2.34-125.6 +- CVE-2025-0395: Fix a buffer overflow in assert (RHEL-83294) + +* Fri Mar 28 2025 Arjun Shankar - 2.34-125.5 +- Make test tst-cpuclock2 run more reliably (RHEL-84324) + +* Wed Feb 26 2025 Patsy Griffin - 2.34-125.4 +- stdlib: Make getenv thread-safe in more cases (RHEL-70707) + * Thu Feb 13 2025 Carlos O'Donell - 2.34-125.3 - Fix missed wakeup in POSIX thread condition variables (RHEL-78939)