Bug#1109941: unblock: glibc/2.41-11 (pre-approval)
Package: release.debian.org
Severity: normal
X-Debbugs-Cc: glibc@packages.debian.org
Control: affects -1 + src:glibc
User: release.debian.org@packages.debian.org
Usertags: unblock
Please unblock package glibc
[ Reason ]
The reason that triggered this upload is a security issue in regcomp
(CVE-2025-8058) that got fixed in the upstream stable branch. It also
includes a fix for iconv creating files with the wrong permissions.
[ Impact ]
If the unblock isn't granted, systems will be vulnerable to
CVE-2025-8058.
[ Tests ]
Tests have been added for both changes, and actually represent the
largest part of the debdiff.
[ Risks ]
Risks are quite low, besides the new tests, changes are small, easily
reviewable and covered by additional tests.
[ Checklist ]
[x] all changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the package in testing
[ Other info ]
If it comes to late for the initial Trixie release, this
could go in the first point release.
unblock glibc/2.41-11
diff --git a/debian/changelog b/debian/changelog
index 4ee8be6e..85356f8e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+glibc (2.41-11) unstable; urgency=medium
+
+ * debian/patches/git-updates.diff: update from upstream stable branch:
+ - Fix iconv to not create executable files with -o.
+ - Fix double-free after allocation failure in regcomp (GLIBC-SA-2025-0005
+ / CVE-2025-8058). Closes: #1109803.
+
+ -- Aurelien Jarno <aurel32@debian.org> Sat, 26 Jul 2025 20:29:12 +0200
+
glibc (2.41-10) unstable; urgency=medium
[ Samuel Thibault ]
diff --git a/debian/patches/git-updates.diff b/debian/patches/git-updates.diff
index f8df40b4..ac1e1c31 100644
--- a/debian/patches/git-updates.diff
+++ b/debian/patches/git-updates.diff
@@ -22,10 +22,10 @@ index d0108d2caa..aa547a443f 100644
$(common-objdir):$(subst $(empty) ,:,$(patsubst ../$(subdir),.,$(rpath-dirs:%=$(common-objpfx)%)))
else # build-static
diff --git a/NEWS b/NEWS
-index b11422b060..90d090ea77 100644
+index b11422b060..89d0935beb 100644
--- a/NEWS
+++ b/NEWS
-@@ -5,6 +5,34 @@ See the end for copying conditions.
+@@ -5,6 +5,36 @@ See the end for copying conditions.
Please send GNU C library bug reports via <https://sourceware.org/bugzilla/>
using `glibc' in the "product" field.
@@ -56,6 +56,8 @@ index b11422b060..90d090ea77 100644
+ [32981] ports: elf/tst-execstack-prog-static-tunable fails on
+ sparc64-linux-gnu
+ [32987] elf: Fix subprocess status handling for tst-dlopen-sgid
++ [33164] iconv -o should not create executable files
++ [33185] Fix double-free after allocation failure in regcomp
+
Version 2.41
@@ -1589,6 +1591,43 @@ index 9f5990f340..8df6f5906e 100644
+glibc.rtld.execstack: 1 (min: 0, max: 2)
glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+)
+diff --git a/iconv/iconv_prog.c b/iconv/iconv_prog.c
+index 7dba5d8dff..558cfb11a3 100644
+--- a/iconv/iconv_prog.c
++++ b/iconv/iconv_prog.c
+@@ -436,7 +436,7 @@ input_error (const char *path)
+ static void
+ open_output_direct (void)
+ {
+- output_fd = open64 (output_file, O_WRONLY | O_CREAT | O_TRUNC, 0777);
++ output_fd = open64 (output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (output_fd < 0)
+ output_error ();
+ }
+@@ -457,7 +457,7 @@ prepare_output_file (char **argv)
+ else
+ {
+ /* If iconv creates the output file, no overlap is possible. */
+- output_fd = open64 (output_file, O_WRONLY | O_CREAT | O_EXCL, 0777);
++ output_fd = open64 (output_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
+ if (output_fd >= 0)
+ output_buffer_size = copy_buffer_size;
+ else
+diff --git a/iconv/tst-iconv_prog-buffer.sh b/iconv/tst-iconv_prog-buffer.sh
+index 1c499d590d..40340c38fa 100644
+--- a/iconv/tst-iconv_prog-buffer.sh
++++ b/iconv/tst-iconv_prog-buffer.sh
+@@ -75,6 +75,10 @@ run_iconv () {
+ }
+
+ check_out_expected () {
++ if test -x "$tmp/out" ; then
++ echo "error: iconv output file is executable"
++ failure=true
++ fi
+ if ! cmp -s "$tmp/out" "$tmp/expected" ; then
+ echo "error: iconv output difference" >&$logfd
+ echo "*** expected ***" >&$logfd
diff --git a/math/auto-libm-test-in b/math/auto-libm-test-in
index 01ba689aa8..4f194da19d 100644
--- a/math/auto-libm-test-in
@@ -1797,6 +1836,18 @@ index e98e2df152..43dd16d59c 100644
iattr->stacksize = to - (size_t) iattr->stackaddr;
#endif
/* We succeed and no need to look further. */
+diff --git a/posix/Makefile b/posix/Makefile
+index a650abf598..0e209a7ed0 100644
+--- a/posix/Makefile
++++ b/posix/Makefile
+@@ -303,6 +303,7 @@ tests := \
+ tst-posix_spawn-setsid \
+ tst-preadwrite \
+ tst-preadwrite64 \
++ tst-regcomp-bracket-free \
+ tst-regcomp-truncated \
+ tst-regex \
+ tst-regex2 \
diff --git a/posix/environ.c b/posix/environ.c
index a0ed0d80ea..924effe3cd 100644
--- a/posix/environ.c
@@ -1816,6 +1867,210 @@ index a0ed0d80ea..924effe3cd 100644
+
+struct environ_array *__environ_array_list;
+environ_counter __environ_counter;
+diff --git a/posix/regcomp.c b/posix/regcomp.c
+index 69675d81f7..5c486cee56 100644
+--- a/posix/regcomp.c
++++ b/posix/regcomp.c
+@@ -3384,6 +3384,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;
+@@ -3399,7 +3400,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 0000000000..3c091d8c44
+--- /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
++ <https://www.gnu.org/licenses/>. */
++
++/* 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 <errno.h>
++#include <regex.h>
++#include <stdio.h>
++#include <string.h>
++#include <support/check.h>
++#include <support/namespace.h>
++#include <support/support.h>
++
++/* 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 <support/test-driver.c>
diff --git a/stdlib/Makefile b/stdlib/Makefile
index 1c4fa2382f..c9c8f702a2 100644
--- a/stdlib/Makefile
Reply to: