Restore compatibility with environ/malloc usage pattern (RHEL-75809)
This commit is contained in:
parent
7e7241f620
commit
95d5cf3a67
245
glibc-RHEL-75809.patch
Normal file
245
glibc-RHEL-75809.patch
Normal file
@ -0,0 +1,245 @@
|
||||
commit b62759db04b8ed7f829c06f1d7c3b8fb70616493
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Wed Jan 22 13:48:56 2025 +0100
|
||||
|
||||
stdlib: Support malloc-managed environ arrays for compatibility
|
||||
|
||||
Some applications set environ to a heap-allocated pointer, call
|
||||
setenv (expecting it to call realloc), free environ, and then
|
||||
restore the original environ pointer. This breaks after
|
||||
commit 7a61e7f557a97ab597d6fca5e2d1f13f65685c61 ("stdlib: Make
|
||||
getenv thread-safe in more cases") because after the setenv call,
|
||||
the environ pointer does not point to the start of a heap allocation.
|
||||
Instead, setenv creates a separate allocation and changes environ
|
||||
to point into that. This means that the free call in the application
|
||||
results in heap corruption.
|
||||
|
||||
The interim approach was more compatible with other libcs because
|
||||
it does not assume that the incoming environ pointer is allocated
|
||||
as if by malloc (if it was written by the application). However,
|
||||
it seems to be more important to stay compatible with previous
|
||||
glibc version: assume the incoming pointer is heap allocated,
|
||||
and preserve this property after setenv calls.
|
||||
|
||||
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
|
||||
|
||||
diff --git a/csu/init-first.c b/csu/init-first.c
|
||||
index a2cb456ccf9ac5e6..77b5b4941beb3a73 100644
|
||||
--- a/csu/init-first.c
|
||||
+++ b/csu/init-first.c
|
||||
@@ -61,6 +61,7 @@ _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 d784de0f0bdd70c8..260027c2396e1f52 100644
|
||||
--- a/csu/libc-start.c
|
||||
+++ b/csu/libc-start.c
|
||||
@@ -244,6 +244,7 @@ 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 e241603b8131a9e9..ada957f9d04d272a 100644
|
||||
--- a/include/unistd.h
|
||||
+++ b/include/unistd.h
|
||||
@@ -203,6 +203,9 @@ 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 a0ed0d80eab207f8..2430b47d8eee148c 100644
|
||||
--- a/posix/environ.c
|
||||
+++ b/posix/environ.c
|
||||
@@ -10,3 +10,5 @@ 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 ff1418f5bb2ea5c9..217600ba60e3c7d4 100644
|
||||
--- a/stdlib/Makefile
|
||||
+++ b/stdlib/Makefile
|
||||
@@ -312,6 +312,7 @@ 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 d12401ca77cee5a7..79982aa12ac20078 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 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
|
||||
+ /* 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. */
|
||||
- target_array = __environ_new_array (required_size);
|
||||
+ /* 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);
|
||||
+ }
|
||||
+ 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)
|
||||
+ {
|
||||
+ 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 = target_array->array + i;
|
||||
+ ep = result_environ + (required_size - 2);
|
||||
|
||||
/* 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
|
||||
new file mode 100644
|
||||
index 0000000000000000..18a9d36842e67aa5
|
||||
--- /dev/null
|
||||
+++ b/stdlib/tst-setenv-malloc.c
|
||||
@@ -0,0 +1,64 @@
|
||||
+/* 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>
|
@ -145,7 +145,7 @@ Version: %{glibcversion}
|
||||
# - It allows using the Release number without the %%dist tag in the dependency
|
||||
# generator to make the generated requires interchangeable between Rawhide
|
||||
# and ELN (.elnYY < .fcXX).
|
||||
%global baserelease 34
|
||||
%global baserelease 35
|
||||
Release: %{baserelease}%{?dist}
|
||||
|
||||
# Licenses:
|
||||
@ -504,6 +504,7 @@ Patch186: glibc-upstream-2.39-143.patch
|
||||
Patch187: glibc-upstream-2.39-144.patch
|
||||
Patch188: glibc-upstream-2.39-145.patch
|
||||
Patch189: glibc-upstream-2.39-146.patch
|
||||
Patch190: glibc-RHEL-75809.patch
|
||||
|
||||
##############################################################################
|
||||
# Continued list of core "glibc" package information:
|
||||
@ -2499,6 +2500,9 @@ update_gconv_modules_cache ()
|
||||
%endif
|
||||
|
||||
%changelog
|
||||
* Thu Jan 23 2025 Florian Weimer <fweimer@redhat.com> - 2.39-35
|
||||
- Restore compatibility with environ/malloc usage pattern (RHEL-75809)
|
||||
|
||||
* Thu Jan 23 2025 Florian Weimer <fweimer@redhat.com> - 2.39-34
|
||||
- Sync with upstream branch release/2.39/master,
|
||||
commit 808a84a8b81468b517a4d721fdc62069cb8c211f:
|
||||
|
Loading…
Reference in New Issue
Block a user