Apply patch from upstream BZ #32470
This commit is contained in:
commit
4c4fd57221
29
SOURCES/glibc-RHEL-65285-1.patch
Normal file
29
SOURCES/glibc-RHEL-65285-1.patch
Normal 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
|
152
SOURCES/glibc-RHEL-65285-2.patch
Normal file
152
SOURCES/glibc-RHEL-65285-2.patch
Normal 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
|
||||
}
|
||||
|
136
SOURCES/glibc-RHEL-65285-3.patch
Normal file
136
SOURCES/glibc-RHEL-65285-3.patch
Normal 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 */
|
54
SOURCES/glibc-RHEL-65285-4.patch
Normal file
54
SOURCES/glibc-RHEL-65285-4.patch
Normal 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;
|
||||
}
|
28
SOURCES/glibc-RHEL-65285-5.patch
Normal file
28
SOURCES/glibc-RHEL-65285-5.patch
Normal 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);
|
||||
|
32
SOURCES/glibc-RHEL-70707-1.patch
Normal file
32
SOURCES/glibc-RHEL-70707-1.patch
Normal 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 *));
|
||||
|
98
SOURCES/glibc-RHEL-70707-2.patch
Normal file
98
SOURCES/glibc-RHEL-70707-2.patch
Normal 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;
|
75
SOURCES/glibc-RHEL-70707-3.patch
Normal file
75
SOURCES/glibc-RHEL-70707-3.patch
Normal 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
|
||||
}
|
||||
|
882
SOURCES/glibc-RHEL-70707-4.patch
Normal file
882
SOURCES/glibc-RHEL-70707-4.patch
Normal 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>
|
56
SOURCES/glibc-RHEL-83294-1.patch
Normal file
56
SOURCES/glibc-RHEL-83294-1.patch
Normal 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);
|
54
SOURCES/glibc-RHEL-83294-2.patch
Normal file
54
SOURCES/glibc-RHEL-83294-2.patch
Normal 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
|
124
SOURCES/glibc-RHEL-83294-3.patch
Normal file
124
SOURCES/glibc-RHEL-83294-3.patch
Normal 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>
|
45
SOURCES/glibc-RHEL-83524.patch
Normal file
45
SOURCES/glibc-RHEL-83524.patch
Normal 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 */
|
161
SOURCES/glibc-RHEL-84324.patch
Normal file
161
SOURCES/glibc-RHEL-84324.patch
Normal 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);
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user