From 830d6e89e2afaadd45e974078d5c262d9e0898bc Mon Sep 17 00:00:00 2001 From: Qingqing Li Date: Wed, 30 Jul 2025 14:12:32 +0800 Subject: [PATCH] posix: Fix double-free after allocation failure in regcomp (bug 33185) This also fixed CVE-2025-8058 (cherry picked from commit 368a100575f09fb99ef8986d547718691772f46b) --- glibc.spec | 6 +- ...-free-after-allocation-failure-in-re.patch | 255 ++++++++++++++++++ 2 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 posix-Fix-double-free-after-allocation-failure-in-re.patch diff --git a/glibc.spec b/glibc.spec index 10bfe9c..6d953f5 100644 --- a/glibc.spec +++ b/glibc.spec @@ -71,7 +71,7 @@ ############################################################################## Name: glibc Version: 2.34 -Release: 169 +Release: 170 Summary: The GNU libc libraries License: %{all_license} URL: http://www.gnu.org/software/glibc/ @@ -323,6 +323,7 @@ Patch231: backport-support-Use-const-char-argument-in-support_capture_s.patch Patch232: backport-support-Add-support_record_failure_barrier.patch Patch233: backport-elf-Test-case-for-bug-32976-CVE-2025-4802.patch Patch234: backport-0001-posix-Remove-alloca-usage-for-internal-fnmatch-imple.patch +Patch235: posix-Fix-double-free-after-allocation-failure-in-re.patch Patch9000: turn-default-value-of-x86_rep_stosb_threshold_form_2K_to_1M.patch Patch9001: delete-no-hard-link-to-avoid-all_language-package-to.patch @@ -1555,6 +1556,9 @@ fi %endif %changelog +* Wed Jul 30 2025 Qingqing Li 2.34-170 +- posix: Fix double-free after allocation failure in regcomp (bug 33185) CVE-2025-8058 + * Wed Jul 23 2025 liumingran 2.34-169 - Type:bugfix - ID:NA diff --git a/posix-Fix-double-free-after-allocation-failure-in-re.patch b/posix-Fix-double-free-after-allocation-failure-in-re.patch new file mode 100644 index 0000000..2d05138 --- /dev/null +++ b/posix-Fix-double-free-after-allocation-failure-in-re.patch @@ -0,0 +1,255 @@ +From 90db02ef73907d830b1d8d86d554ba35626b006f Mon Sep 17 00:00:00 2001 +From: Florian Weimer +Date: Mon, 21 Jul 2025 21:43:49 +0200 +Subject: [PATCH] posix: Fix double-free after allocation failure in +regcomp (bug 33185) + + If a memory allocation failure occurs during bracket expression + parsing in regcomp, a double-free error may result. + + Reported-by: Anastasia Belova + Co-authored-by: Paul Eggert + Reviewed-by: Andreas K. Huettel + (cherry picked from commit 7ea06e994093fa0bcca0d0ee2c1db271d8d7885d) + + Conflict: adapt context for NEWS +--- + NEWS | 1 + + posix/Makefile | 2 +- + posix/regcomp.c | 4 +- + posix/tst-regcomp-bracket-free.c | 176 +++++++++++++++++++++++++++++++ + 4 files changed, 181 insertions(+), 2 deletions(-) + create mode 100644 posix/tst-regcomp-bracket-free.c + +diff --git a/NEWS b/NEWS +index 6e2a0967..6884df48 100644 +--- a/NEWS ++++ b/NEWS +@@ -47,6 +47,7 @@ The following bugs are resolved with this release: + [29097] time: fchmodat does not handle 64 bit time_t for + AT_SYMLINK_NOFOLLOW + [32582] Fix underallocation of abort_msg_s struct (CVE-2025-0395) ++ [33185] Fix double-free after allocation failure in regcomp + + + Version 2.34 +diff --git a/posix/Makefile b/posix/Makefile +index 61fcdf01..ac9a3d9e 100644 +--- a/posix/Makefile ++++ b/posix/Makefile +@@ -108,7 +108,7 @@ tests := test-errno tstgetopt testfnm runtests runptests \ + tst-glob-tilde test-ssize-max tst-spawn4 bug-regex37 \ + bug-regex38 tst-regcomp-truncated tst-spawn-chdir \ + tst-wordexp-nocmd tst-execveat tst-spawn5 \ +- tst-sched_getaffinity ++ tst-sched_getaffinity tst-regcomp-bracket-free + + # Test for the glob symbol version that was replaced in glibc 2.27. + ifeq ($(have-GLIBC_2.26)$(build-shared),yesyes) +diff --git a/posix/regcomp.c b/posix/regcomp.c +index d93698ae..e47c45f8 100644 +--- a/posix/regcomp.c ++++ b/posix/regcomp.c +@@ -3367,6 +3367,7 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, + { + #ifdef RE_ENABLE_I18N + free_charset (mbcset); ++ mbcset = NULL; + #endif + /* Build a tree for simple bracket. */ + br_token.type = SIMPLE_BRACKET; +@@ -3382,7 +3383,8 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, + parse_bracket_exp_free_return: + re_free (sbcset); + #ifdef RE_ENABLE_I18N +- free_charset (mbcset); ++ if (__glibc_likely (mbcset != NULL)) ++ free_charset (mbcset); + #endif /* RE_ENABLE_I18N */ + return NULL; + } +diff --git a/posix/tst-regcomp-bracket-free.c b/posix/tst-regcomp-bracket-free.c +new file mode 100644 +index 00000000..3c091d8c +--- /dev/null ++++ b/posix/tst-regcomp-bracket-free.c +@@ -0,0 +1,176 @@ ++/* Test regcomp bracket parsing with injected allocation failures (bug 33185). ++ 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 invokes regcomp multiple times, failing one memory ++ allocation in each call. The function call should fail with ++ REG_ESPACE (or succeed if it can recover from the allocation ++ failure). Previously, there was double-free bug. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Data structure allocated via MAP_SHARED, so that writes from the ++ subprocess are visible. */ ++struct shared_data ++{ ++ /* Number of tracked allocations performed so far. */ ++ volatile unsigned int allocation_count; ++ ++ /* If this number is reached, one allocation fails. */ ++ volatile unsigned int failing_allocation; ++ ++ /* The subprocess stores the expected name here. */ ++ char name[100]; ++}; ++ ++/* Allocation count in shared mapping. */ ++static struct shared_data *shared; ++ ++/* Returns true if a failure should be injected for this allocation. */ ++static bool ++fail_this_allocation (void) ++{ ++ if (shared != NULL) ++ { ++ unsigned int count = shared->allocation_count; ++ shared->allocation_count = count + 1; ++ return count == shared->failing_allocation; ++ } ++ else ++ return false; ++} ++ ++/* Failure-injecting wrappers for allocation functions used by glibc. */ ++ ++void * ++malloc (size_t size) ++{ ++ if (fail_this_allocation ()) ++ { ++ errno = ENOMEM; ++ return NULL; ++ } ++ extern __typeof (malloc) __libc_malloc; ++ return __libc_malloc (size); ++} ++ ++void * ++calloc (size_t a, size_t b) ++{ ++ if (fail_this_allocation ()) ++ { ++ errno = ENOMEM; ++ return NULL; ++ } ++ extern __typeof (calloc) __libc_calloc; ++ return __libc_calloc (a, b); ++} ++ ++void * ++realloc (void *ptr, size_t size) ++{ ++ if (fail_this_allocation ()) ++ { ++ errno = ENOMEM; ++ return NULL; ++ } ++ extern __typeof (realloc) __libc_realloc; ++ return __libc_realloc (ptr, size); ++} ++ ++/* No-op subprocess to verify that support_isolate_in_subprocess does ++ not perform any heap allocations. */ ++static void ++no_op (void *ignored) ++{ ++} ++ ++/* Perform a regcomp call in a subprocess. Used to count its ++ allocations. */ ++static void ++initialize (void *regexp1) ++{ ++ const char *regexp = regexp1; ++ ++ shared->allocation_count = 0; ++ ++ regex_t reg; ++ TEST_COMPARE (regcomp (®, regexp, 0), 0); ++} ++ ++/* Perform regcomp in a subprocess with fault injection. */ ++static void ++test_in_subprocess (void *regexp1) ++{ ++ const char *regexp = regexp1; ++ unsigned int inject_at = shared->failing_allocation; ++ ++ regex_t reg; ++ int ret = regcomp (®, regexp, 0); ++ ++ if (ret != 0) ++ { ++ TEST_COMPARE (ret, REG_ESPACE); ++ printf ("info: allocation %u failure results in return value %d," ++ " error %s (%d)\n", ++ inject_at, ret, strerrorname_np (errno), errno); ++ } ++} ++ ++static int ++do_test (void) ++{ ++ char regexp[] = "[:alpha:]"; ++ ++ shared = support_shared_allocate (sizeof (*shared)); ++ ++ /* Disable fault injection. */ ++ shared->failing_allocation = ~0U; ++ ++ support_isolate_in_subprocess (no_op, NULL); ++ TEST_COMPARE (shared->allocation_count, 0); ++ ++ support_isolate_in_subprocess (initialize, regexp); ++ ++ /* The number of allocations in the successful case, plus some ++ slack. Once the number of expected allocations is exceeded, ++ injecting further failures does not make a difference. */ ++ unsigned int maximum_allocation_count = shared->allocation_count; ++ printf ("info: successful call performs %u allocations\n", ++ maximum_allocation_count); ++ maximum_allocation_count += 10; ++ ++ for (unsigned int inject_at = 0; inject_at <= maximum_allocation_count; ++ ++inject_at) ++ { ++ shared->allocation_count = 0; ++ shared->failing_allocation = inject_at; ++ support_isolate_in_subprocess (test_in_subprocess, regexp); ++ } ++ ++ support_shared_free (shared); ++ ++ return 0; ++} ++ ++#include +-- +2.27.0 + -- Gitee