diff --git a/glibc-RHEL-75809.patch b/glibc-RHEL-75809.patch new file mode 100644 index 0000000..c66091d --- /dev/null +++ b/glibc-RHEL-75809.patch @@ -0,0 +1,245 @@ +commit b62759db04b8ed7f829c06f1d7c3b8fb70616493 +Author: Florian Weimer +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 + +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 ++ . */ ++ ++/* 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 ++#include ++#include ++#include ++ ++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 diff --git a/glibc.spec b/glibc.spec index ef89355..0a924d9 100644 --- a/glibc.spec +++ b/glibc.spec @@ -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 - 2.39-35 +- Restore compatibility with environ/malloc usage pattern (RHEL-75809) + * Thu Jan 23 2025 Florian Weimer - 2.39-34 - Sync with upstream branch release/2.39/master, commit 808a84a8b81468b517a4d721fdc62069cb8c211f: