setenv: Rework free(environ) compatibility support (#2341908)
Resolves: RHEL-75809
This commit is contained in:
parent
b76546a663
commit
2fac143e74
232
glibc-RHEL-75809-2.patch
Normal file
232
glibc-RHEL-75809-2.patch
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
commit 36fcdfbbc5463e55581fec67141df3493fb81f7e
|
||||||
|
Author: Florian Weimer <fweimer@redhat.com>
|
||||||
|
Date: Fri Jan 24 08:04:23 2025 +0100
|
||||||
|
|
||||||
|
Revert "stdlib: Support malloc-managed environ arrays for compatibility"
|
||||||
|
|
||||||
|
This reverts commit b62759db04b8ed7f829c06f1d7c3b8fb70616493.
|
||||||
|
|
||||||
|
Reason for revert: Incompatible with “env -i” and coreutils (bug 32588).
|
||||||
|
|
||||||
|
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
|
||||||
|
|
||||||
|
diff --git a/csu/init-first.c b/csu/init-first.c
|
||||||
|
index 77b5b4941beb3a73..a2cb456ccf9ac5e6 100644
|
||||||
|
--- a/csu/init-first.c
|
||||||
|
+++ b/csu/init-first.c
|
||||||
|
@@ -61,7 +61,6 @@ _init_first (int argc, char **argv, char **envp)
|
||||||
|
__libc_argc = argc;
|
||||||
|
__libc_argv = argv;
|
||||||
|
__environ = envp;
|
||||||
|
- __environ_startup = envp;
|
||||||
|
|
||||||
|
#ifndef SHARED
|
||||||
|
/* First the initialization which normally would be done by the
|
||||||
|
diff --git a/csu/libc-start.c b/csu/libc-start.c
|
||||||
|
index 260027c2396e1f52..d784de0f0bdd70c8 100644
|
||||||
|
--- a/csu/libc-start.c
|
||||||
|
+++ b/csu/libc-start.c
|
||||||
|
@@ -244,7 +244,6 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
|
||||||
|
char **ev = &argv[argc + 1];
|
||||||
|
|
||||||
|
__environ = ev;
|
||||||
|
- __environ_startup = ev;
|
||||||
|
|
||||||
|
/* Store the lowest stack address. This is done in ld.so if this is
|
||||||
|
the code for the DSO. */
|
||||||
|
diff --git a/include/unistd.h b/include/unistd.h
|
||||||
|
index ada957f9d04d272a..e241603b8131a9e9 100644
|
||||||
|
--- a/include/unistd.h
|
||||||
|
+++ b/include/unistd.h
|
||||||
|
@@ -203,9 +203,6 @@ libc_hidden_proto (__tcsetpgrp)
|
||||||
|
extern int __libc_enable_secure attribute_relro;
|
||||||
|
rtld_hidden_proto (__libc_enable_secure)
|
||||||
|
|
||||||
|
-/* Original value of __environ. Initialized by _init_first (dynamic)
|
||||||
|
- or __libc_start_main (static). */
|
||||||
|
-extern char **__environ_startup attribute_hidden;
|
||||||
|
|
||||||
|
/* Various internal function. */
|
||||||
|
extern void __libc_check_standard_fds (void) attribute_hidden;
|
||||||
|
diff --git a/posix/environ.c b/posix/environ.c
|
||||||
|
index 2430b47d8eee148c..a0ed0d80eab207f8 100644
|
||||||
|
--- a/posix/environ.c
|
||||||
|
+++ b/posix/environ.c
|
||||||
|
@@ -10,5 +10,3 @@ weak_alias (__environ, environ)
|
||||||
|
/* The SVR4 ABI says `_environ' will be the name to use
|
||||||
|
in case the user overrides the weak alias `environ'. */
|
||||||
|
weak_alias (__environ, _environ)
|
||||||
|
-
|
||||||
|
-char **__environ_startup;
|
||||||
|
diff --git a/stdlib/Makefile b/stdlib/Makefile
|
||||||
|
index 217600ba60e3c7d4..ff1418f5bb2ea5c9 100644
|
||||||
|
--- a/stdlib/Makefile
|
||||||
|
+++ b/stdlib/Makefile
|
||||||
|
@@ -312,7 +312,6 @@ tests := \
|
||||||
|
tst-setcontext9 \
|
||||||
|
tst-setcontext10 \
|
||||||
|
tst-setcontext11 \
|
||||||
|
- tst-setenv-malloc \
|
||||||
|
tst-stdbit-Wconversion \
|
||||||
|
tst-stdbit-builtins \
|
||||||
|
tst-stdc_bit_ceil \
|
||||||
|
diff --git a/stdlib/setenv.c b/stdlib/setenv.c
|
||||||
|
index 79982aa12ac20078..d12401ca77cee5a7 100644
|
||||||
|
--- a/stdlib/setenv.c
|
||||||
|
+++ b/stdlib/setenv.c
|
||||||
|
@@ -191,52 +191,52 @@ __add_to_environ (const char *name, const char *value, const char *combined,
|
||||||
|
ep[1] = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
- /* We cannot use __environ as is and need a larger allocation. */
|
||||||
|
-
|
||||||
|
- if (start_environ == __environ_startup
|
||||||
|
- || __environ_is_from_array_list (start_environ))
|
||||||
|
- {
|
||||||
|
- /* Allocate a new array, managed in the list. */
|
||||||
|
- struct environ_array *target_array
|
||||||
|
- = __environ_new_array (required_size);
|
||||||
|
- if (target_array == NULL)
|
||||||
|
- {
|
||||||
|
- UNLOCK;
|
||||||
|
- return -1;
|
||||||
|
- }
|
||||||
|
- result_environ = &target_array->array[0];
|
||||||
|
-
|
||||||
|
- /* Copy over the __environ array contents. 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. */
|
||||||
|
- result_environ[i] = atomic_load_relaxed (start_environ + i);
|
||||||
|
- }
|
||||||
|
+ /* 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
|
||||||
|
{
|
||||||
|
- /* Otherwise the application installed its own pointer.
|
||||||
|
- Historically, this pointer was managed using realloc.
|
||||||
|
- Continue doing so. This disables multi-threading
|
||||||
|
- support. */
|
||||||
|
- result_environ = __libc_reallocarray (start_environ,
|
||||||
|
- required_size,
|
||||||
|
- sizeof (*result_environ));
|
||||||
|
- if (result_environ == NULL)
|
||||||
|
+ /* Allocate a new array. */
|
||||||
|
+ target_array = __environ_new_array (required_size);
|
||||||
|
+ if (target_array == NULL)
|
||||||
|
{
|
||||||
|
UNLOCK;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* 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 = result_environ + (required_size - 2);
|
||||||
|
+ ep = target_array->array + i;
|
||||||
|
|
||||||
|
/* Add the null terminator in case there was a pointer there
|
||||||
|
previously. */
|
||||||
|
ep[1] = NULL;
|
||||||
|
+
|
||||||
|
+ /* And __environ should be repointed to our array. */
|
||||||
|
+ result_environ = &target_array->array[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/stdlib/tst-setenv-malloc.c b/stdlib/tst-setenv-malloc.c
|
||||||
|
deleted file mode 100644
|
||||||
|
index 18a9d36842e67aa5..0000000000000000
|
||||||
|
--- a/stdlib/tst-setenv-malloc.c
|
||||||
|
+++ /dev/null
|
||||||
|
@@ -1,64 +0,0 @@
|
||||||
|
-/* Test using setenv with a malloc-allocated environ variable.
|
||||||
|
- Copyright (C) 2025 Free Software Foundation, Inc.
|
||||||
|
- This file is part of the GNU C Library.
|
||||||
|
-
|
||||||
|
- The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
- modify it under the terms of the GNU Lesser General Public
|
||||||
|
- License as published by the Free Software Foundation; either
|
||||||
|
- version 2.1 of the License, or (at your option) any later version.
|
||||||
|
-
|
||||||
|
- The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
- Lesser General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Lesser General Public
|
||||||
|
- License along with the GNU C Library; if not, see
|
||||||
|
- <https://www.gnu.org/licenses/>. */
|
||||||
|
-
|
||||||
|
-/* This test is not in the scope for POSIX or any other standard, but
|
||||||
|
- some applications assume that environ is a heap-allocated pointer
|
||||||
|
- after a call to setenv on an empty environment. */
|
||||||
|
-
|
||||||
|
-#include <stdlib.h>
|
||||||
|
-#include <unistd.h>
|
||||||
|
-#include <support/check.h>
|
||||||
|
-#include <support/support.h>
|
||||||
|
-
|
||||||
|
-static const char *original_path;
|
||||||
|
-static char **save_environ;
|
||||||
|
-
|
||||||
|
-static void
|
||||||
|
-rewrite_environ (void)
|
||||||
|
-{
|
||||||
|
- save_environ = environ;
|
||||||
|
- environ = xmalloc (sizeof (*environ));
|
||||||
|
- *environ = NULL;
|
||||||
|
- TEST_COMPARE (setenv ("A", "1", 1), 0);
|
||||||
|
- TEST_COMPARE (setenv ("B", "2", 1), 0);
|
||||||
|
- TEST_VERIFY (environ != save_environ);
|
||||||
|
- TEST_COMPARE_STRING (environ[0], "A=1");
|
||||||
|
- TEST_COMPARE_STRING (environ[1], "B=2");
|
||||||
|
- TEST_COMPARE_STRING (environ[2], NULL);
|
||||||
|
- TEST_COMPARE_STRING (getenv ("PATH"), NULL);
|
||||||
|
- free (environ);
|
||||||
|
- environ = save_environ;
|
||||||
|
- TEST_COMPARE_STRING (getenv ("PATH"), original_path);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int
|
||||||
|
-do_test (void)
|
||||||
|
-{
|
||||||
|
- original_path = getenv ("PATH");
|
||||||
|
- rewrite_environ ();
|
||||||
|
-
|
||||||
|
- /* Test again after reallocated the environment due to an initial
|
||||||
|
- setenv call. */
|
||||||
|
- TEST_COMPARE (setenv ("TST_SETENV_MALLOC", "1", 1), 0);
|
||||||
|
- TEST_VERIFY (environ != save_environ);
|
||||||
|
- rewrite_environ ();
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-#include <support/test-driver.c>
|
299
glibc-RHEL-75809-3.patch
Normal file
299
glibc-RHEL-75809-3.patch
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
commit 12b4a1fc6ecfc278a87159164bdf1d682deb18e2
|
||||||
|
Author: Florian Weimer <fweimer@redhat.com>
|
||||||
|
Date: Fri Jan 24 10:40:28 2025 +0100
|
||||||
|
|
||||||
|
stdlib: Re-implement free (environ) compatibility kludge for setenv
|
||||||
|
|
||||||
|
For the originally failing application (userhelper from usermode),
|
||||||
|
it is not actually necessary to call realloc on the environ
|
||||||
|
pointer. Yes, there will be a memory leak because the application
|
||||||
|
assigns a heap-allocated pointer to environ that it never frees,
|
||||||
|
but this leak was always there: the old realloc-based setenv had
|
||||||
|
a hidden internal variable, last_environ, that was used in a similar
|
||||||
|
way to __environ_array_list. The application is not impacted by
|
||||||
|
the leak anyway because the relevant operations do not happen in
|
||||||
|
a loop.
|
||||||
|
|
||||||
|
The change here just uses a separte heap allocation and points
|
||||||
|
environ to that. This means that if an application calls
|
||||||
|
free (environ) and restores the environ pointer to the value
|
||||||
|
at process start, and does not modify the environment further,
|
||||||
|
nothing bad happens.
|
||||||
|
|
||||||
|
This change should not invalidate any previous testing that went into
|
||||||
|
the original getenv thread safety change, commit 7a61e7f557a97ab597d6
|
||||||
|
("stdlib: Make getenv thread-safe in more cases").
|
||||||
|
|
||||||
|
The new test cases are modeled in part on the env -i use case from
|
||||||
|
bug 32588 (with !DO_MALLOC && !DO_EARLY_SETENV), and the previous
|
||||||
|
stdlib/tst-setenv-malloc test. The DO_MALLOC && !DO_EARLY_SETENV
|
||||||
|
case in the new test should approximate what userhelper from the
|
||||||
|
usermode package does.
|
||||||
|
|
||||||
|
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
|
||||||
|
|
||||||
|
diff --git a/stdlib/Makefile b/stdlib/Makefile
|
||||||
|
index ff1418f5bb2ea5c9..f4dec9be46a573b9 100644
|
||||||
|
--- a/stdlib/Makefile
|
||||||
|
+++ b/stdlib/Makefile
|
||||||
|
@@ -275,6 +275,10 @@ tests := \
|
||||||
|
tst-canon-bz26341 \
|
||||||
|
tst-cxa_atexit \
|
||||||
|
tst-environ \
|
||||||
|
+ tst-environ-change-1 \
|
||||||
|
+ tst-environ-change-2 \
|
||||||
|
+ tst-environ-change-3 \
|
||||||
|
+ tst-environ-change-4 \
|
||||||
|
tst-getenv-signal \
|
||||||
|
tst-getenv-thread \
|
||||||
|
tst-getenv-unsetenv \
|
||||||
|
diff --git a/stdlib/setenv.c b/stdlib/setenv.c
|
||||||
|
index d12401ca77cee5a7..20b0e1673c9557de 100644
|
||||||
|
--- a/stdlib/setenv.c
|
||||||
|
+++ b/stdlib/setenv.c
|
||||||
|
@@ -118,24 +118,21 @@ __environ_new_array (size_t required_size)
|
||||||
|
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))
|
||||||
|
+ /* Zero-initialize everything, so that getenv can only
|
||||||
|
+ observe valid or null pointers. */
|
||||||
|
+ char **new_array = calloc (new_size, sizeof (*new_array));
|
||||||
|
+ if (new_array == NULL)
|
||||||
|
+ return NULL;
|
||||||
|
+
|
||||||
|
+ struct environ_array *target_array = malloc (sizeof (*target_array));
|
||||||
|
+ if (target_array == NULL)
|
||||||
|
{
|
||||||
|
- __set_errno (ENOMEM);
|
||||||
|
+ free (new_array);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* 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;
|
||||||
|
+ target_array->array = new_array;
|
||||||
|
assert (new_size >= target_array->allocated);
|
||||||
|
|
||||||
|
/* Put it onto the list. */
|
||||||
|
@@ -236,7 +233,7 @@ __add_to_environ (const char *name, const char *value, const char *combined,
|
||||||
|
ep[1] = NULL;
|
||||||
|
|
||||||
|
/* And __environ should be repointed to our array. */
|
||||||
|
- result_environ = &target_array->array[0];
|
||||||
|
+ result_environ = target_array->array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -403,6 +400,7 @@ __libc_setenv_freemem (void)
|
||||||
|
/* Clear all backing arrays. */
|
||||||
|
while (__environ_array_list != NULL)
|
||||||
|
{
|
||||||
|
+ free (__environ_array_list->array);
|
||||||
|
void *ptr = __environ_array_list;
|
||||||
|
__environ_array_list = __environ_array_list->next;
|
||||||
|
free (ptr);
|
||||||
|
diff --git a/stdlib/setenv.h b/stdlib/setenv.h
|
||||||
|
index 036f4274aa29b722..42b86fff1008bc81 100644
|
||||||
|
--- a/stdlib/setenv.h
|
||||||
|
+++ b/stdlib/setenv.h
|
||||||
|
@@ -29,9 +29,18 @@
|
||||||
|
of environment values used before. */
|
||||||
|
struct environ_array
|
||||||
|
{
|
||||||
|
- struct environ_array *next; /* Previously used environment array. */
|
||||||
|
+ /* The actual environment array. Use a separate allocation (and not
|
||||||
|
+ a flexible array member) so that calls like free (environ) that
|
||||||
|
+ have been encountered in some applications do not crash
|
||||||
|
+ immediately. With such a call, if the application restores the
|
||||||
|
+ original environ pointer at process start and does not modify the
|
||||||
|
+ environment again, a use-after-free situation only occurs during
|
||||||
|
+ __libc_freeres, which is only called during memory debugging.
|
||||||
|
+ With subsequent setenv calls, there is still heap corruption, but
|
||||||
|
+ that happened with the old realloc-based implementation, too. */
|
||||||
|
+ char **array;
|
||||||
|
size_t allocated; /* Number of allocated array elments. */
|
||||||
|
- char *array[]; /* The actual environment array. */
|
||||||
|
+ struct environ_array *next; /* Previously used environment array. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* After initialization, and until the user resets environ (perhaps by
|
||||||
|
@@ -44,7 +53,7 @@ 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;
|
||||||
|
+ return eal != NULL && eal->array == ep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Counter for detecting concurrent modification in unsetenv.
|
||||||
|
diff --git a/stdlib/tst-environ-change-1.c b/stdlib/tst-environ-change-1.c
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000..4241ad4c63ea2e33
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/stdlib/tst-environ-change-1.c
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+#define DO_EARLY_SETENV 0
|
||||||
|
+#define DO_MALLOC 0
|
||||||
|
+#include "tst-environ-change-skeleton.c"
|
||||||
|
diff --git a/stdlib/tst-environ-change-2.c b/stdlib/tst-environ-change-2.c
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000..b20be124902125e8
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/stdlib/tst-environ-change-2.c
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+#define DO_EARLY_SETENV 0
|
||||||
|
+#define DO_MALLOC 1
|
||||||
|
+#include "tst-environ-change-skeleton.c"
|
||||||
|
diff --git a/stdlib/tst-environ-change-3.c b/stdlib/tst-environ-change-3.c
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000..e77996a6cb0ac601
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/stdlib/tst-environ-change-3.c
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+#define DO_EARLY_SETENV 1
|
||||||
|
+#define DO_MALLOC 0
|
||||||
|
+#include "tst-environ-change-skeleton.c"
|
||||||
|
diff --git a/stdlib/tst-environ-change-4.c b/stdlib/tst-environ-change-4.c
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000..633ef7bda84eb2a8
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/stdlib/tst-environ-change-4.c
|
||||||
|
@@ -0,0 +1,3 @@
|
||||||
|
+#define DO_EARLY_SETENV 1
|
||||||
|
+#define DO_MALLOC 1
|
||||||
|
+#include "tst-environ-change-skeleton.c"
|
||||||
|
diff --git a/stdlib/tst-environ-change-skeleton.c b/stdlib/tst-environ-change-skeleton.c
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000..c9b02844369207d9
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/stdlib/tst-environ-change-skeleton.c
|
||||||
|
@@ -0,0 +1,118 @@
|
||||||
|
+/* Test deallocation of the environ pointer.
|
||||||
|
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||||
|
+ This file is part of the GNU C Library.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is free software; you can redistribute it and/or
|
||||||
|
+ modify it under the terms of the GNU Lesser General Public
|
||||||
|
+ License as published by the Free Software Foundation; either
|
||||||
|
+ version 2.1 of the License, or (at your option) any later version.
|
||||||
|
+
|
||||||
|
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||||
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
+ Lesser General Public License for more details.
|
||||||
|
+
|
||||||
|
+ You should have received a copy of the GNU Lesser General Public
|
||||||
|
+ License along with the GNU C Library; if not, see
|
||||||
|
+ <https://www.gnu.org/licenses/>. */
|
||||||
|
+
|
||||||
|
+/* This test is not in the scope for POSIX or any other standard, but
|
||||||
|
+ some applications assume that environ is a heap-allocated pointer
|
||||||
|
+ after a call to setenv on an empty environment. They also try to
|
||||||
|
+ save and restore environ in an attempt to undo a temporary
|
||||||
|
+ modification of the environment array, but this does not work if
|
||||||
|
+ setenv was called before.
|
||||||
|
+
|
||||||
|
+ Before including this file, these macros need to be defined
|
||||||
|
+ to 0 or 1:
|
||||||
|
+
|
||||||
|
+ DO_EARLY_SETENV If 1, perform a setenv call before changing environ.
|
||||||
|
+ DO_MALLOC If 1, use a heap pointer for the empty environment.
|
||||||
|
+
|
||||||
|
+ Note that this test will produce errors under valgrind and other
|
||||||
|
+ memory tracers that call __libc_freeres because free (environ)
|
||||||
|
+ deallocates a pointer still used internally. */
|
||||||
|
+
|
||||||
|
+#include <stdlib.h>
|
||||||
|
+#include <unistd.h>
|
||||||
|
+#include <support/check.h>
|
||||||
|
+#include <support/support.h>
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+check_rewritten (void)
|
||||||
|
+{
|
||||||
|
+ TEST_COMPARE_STRING (environ[0], "tst_environ_change_a=1");
|
||||||
|
+ TEST_COMPARE_STRING (environ[1], "tst_environ_change_b=2");
|
||||||
|
+ TEST_COMPARE_STRING (environ[2], NULL);
|
||||||
|
+ TEST_COMPARE_STRING (getenv ("tst_environ_change_a"), "1");
|
||||||
|
+ TEST_COMPARE_STRING (getenv ("tst_environ_change_b"), "2");
|
||||||
|
+ TEST_COMPARE_STRING (getenv ("tst_environ_change_early"), NULL);
|
||||||
|
+ TEST_COMPARE_STRING (getenv ("PATH"), NULL);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int
|
||||||
|
+do_test (void)
|
||||||
|
+{
|
||||||
|
+ TEST_COMPARE_STRING (getenv ("tst_environ_change_a"), NULL);
|
||||||
|
+ TEST_COMPARE_STRING (getenv ("tst_environ_change_b"), NULL);
|
||||||
|
+ TEST_COMPARE_STRING (getenv ("tst_environ_change_early_setenv"), NULL);
|
||||||
|
+#if DO_EARLY_SETENV
|
||||||
|
+ TEST_COMPARE (setenv ("tst_environ_change_early_setenv", "1", 1), 0);
|
||||||
|
+#else
|
||||||
|
+ /* Must come back after environ reset. */
|
||||||
|
+ char *original_path = xstrdup (getenv ("PATH"));
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ char **save_environ = environ;
|
||||||
|
+#if DO_MALLOC
|
||||||
|
+ environ = xmalloc (sizeof (*environ));
|
||||||
|
+#else
|
||||||
|
+ char *environ_array[1];
|
||||||
|
+ environ = environ_array;
|
||||||
|
+#endif
|
||||||
|
+ *environ = NULL;
|
||||||
|
+ TEST_COMPARE (setenv ("tst_environ_change_a", "1", 1), 0);
|
||||||
|
+ TEST_COMPARE (setenv ("tst_environ_change_b", "2", 1), 0);
|
||||||
|
+#if !DO_EARLY_SETENV
|
||||||
|
+ /* Early setenv results in reuse of the heap-allocated environ array
|
||||||
|
+ that does not change as more pointers are added to it. */
|
||||||
|
+ TEST_VERIFY (environ != save_environ);
|
||||||
|
+#endif
|
||||||
|
+ check_rewritten ();
|
||||||
|
+
|
||||||
|
+ bool check_environ = true;
|
||||||
|
+#if DO_MALLOC
|
||||||
|
+ /* Disable further checks if the free call clobbers the environ
|
||||||
|
+ contents. Whether that is the case depends on the internal
|
||||||
|
+ setenv allocation policy and the heap layout. */
|
||||||
|
+ check_environ = environ != save_environ;
|
||||||
|
+ /* Invalid: Causes internal use-after-free condition. Yet this has
|
||||||
|
+ to be supported for compatibility with some applications. */
|
||||||
|
+ free (environ);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ environ = save_environ;
|
||||||
|
+
|
||||||
|
+#if DO_EARLY_SETENV
|
||||||
|
+ /* With an early setenv, the internal environ array was overwritten.
|
||||||
|
+ Historically, this triggered a use-after-free problem because of
|
||||||
|
+ the use of realloc internally in setenv, but it may appear as if
|
||||||
|
+ the original environment had been restored. In the current code,
|
||||||
|
+ we can only support this if the free (environ) above call did not
|
||||||
|
+ clobber the array, otherwise getenv will see invalid pointers.
|
||||||
|
+ Due to the use-after-free, invalid pointers could be seen with
|
||||||
|
+ the old implementation as well, but the triggering conditions
|
||||||
|
+ were different. */
|
||||||
|
+ if (check_environ)
|
||||||
|
+ check_rewritten ();
|
||||||
|
+#else
|
||||||
|
+ TEST_VERIFY (check_environ);
|
||||||
|
+ TEST_COMPARE_STRING (getenv ("PATH"), original_path);
|
||||||
|
+ TEST_COMPARE_STRING (getenv ("tst_environ_change_a"), NULL);
|
||||||
|
+ TEST_COMPARE_STRING (getenv ("tst_environ_change_b"), NULL);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#include <support/test-driver.c>
|
@ -145,7 +145,7 @@ Version: %{glibcversion}
|
|||||||
# - It allows using the Release number without the %%dist tag in the dependency
|
# - It allows using the Release number without the %%dist tag in the dependency
|
||||||
# generator to make the generated requires interchangeable between Rawhide
|
# generator to make the generated requires interchangeable between Rawhide
|
||||||
# and ELN (.elnYY < .fcXX).
|
# and ELN (.elnYY < .fcXX).
|
||||||
%global baserelease 36
|
%global baserelease 37
|
||||||
Release: %{baserelease}%{?dist}
|
Release: %{baserelease}%{?dist}
|
||||||
|
|
||||||
# Licenses:
|
# Licenses:
|
||||||
@ -506,6 +506,8 @@ Patch188: glibc-upstream-2.39-145.patch
|
|||||||
Patch189: glibc-upstream-2.39-146.patch
|
Patch189: glibc-upstream-2.39-146.patch
|
||||||
Patch190: glibc-RHEL-75809.patch
|
Patch190: glibc-RHEL-75809.patch
|
||||||
Patch191: glibc-RHEL-75555.patch
|
Patch191: glibc-RHEL-75555.patch
|
||||||
|
Patch192: glibc-RHEL-75809-2.patch
|
||||||
|
Patch193: glibc-RHEL-75809-3.patch
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# Continued list of core "glibc" package information:
|
# Continued list of core "glibc" package information:
|
||||||
@ -2501,6 +2503,9 @@ update_gconv_modules_cache ()
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Fri Jan 24 2025 Florian Weimer <fweimer@redhat.com> - 2.39-37
|
||||||
|
- setenv: Rework free(environ) compatibility support (RHEL-75809)
|
||||||
|
|
||||||
* Thu Jan 23 2025 Florian Weimer <fweimer@redhat.com> - 2.39-36
|
* Thu Jan 23 2025 Florian Weimer <fweimer@redhat.com> - 2.39-36
|
||||||
- CVE-2025-0577: vDSO getrandom predictable randomness after fork (RHEL-75555)
|
- CVE-2025-0577: vDSO getrandom predictable randomness after fork (RHEL-75555)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user