diff options
Diffstat (limited to 'posix')
-rw-r--r-- | posix/Makefile | 3 | ||||
-rw-r--r-- | posix/fnmatch_loop.c | 8 | ||||
-rw-r--r-- | posix/glob.h | 3 | ||||
-rw-r--r-- | posix/regcomp.c | 11 | ||||
-rw-r--r-- | posix/tst-libc-message.c | 48 | ||||
-rw-r--r-- | posix/tst-regcomp-bracket-free.c | 176 | ||||
-rw-r--r-- | posix/tst-regex.input | 12 | ||||
-rw-r--r-- | posix/tst-truncate-common.c | 2 |
8 files changed, 242 insertions, 21 deletions
diff --git a/posix/Makefile b/posix/Makefile index c0e2242..a36e5de 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -304,6 +304,7 @@ tests := \ tst-posix_spawn-setsid \ tst-preadwrite \ tst-preadwrite64 \ + tst-regcomp-bracket-free \ tst-regcomp-truncated \ tst-regex \ tst-regex2 \ @@ -348,6 +349,7 @@ tests-internal := \ bug-regex5 \ bug-regex20 \ bug-regex33 \ + tst-libc-message \ # tests-internal tests-container := \ @@ -391,6 +393,7 @@ endif tests-static = \ tst-exec-static \ + tst-libc-message \ tst-spawn-static \ # tests-static diff --git a/posix/fnmatch_loop.c b/posix/fnmatch_loop.c index 9ec5e0e..83f8861 100644 --- a/posix/fnmatch_loop.c +++ b/posix/fnmatch_loop.c @@ -537,7 +537,7 @@ FCT (const CHAR *pattern, const CHAR *string, const CHAR *string_end, that it was properly set in the loop above. */ DIAG_PUSH_NEEDS_COMMENT; - DIAG_IGNORE_Os_NEEDS_COMMENT (8, "-Wmaybe-uninitialized"); + DIAG_IGNORE_NEEDS_COMMENT (16, "-Wmaybe-uninitialized"); if (! is_range # if WIDE_CHAR_VERSION @@ -560,7 +560,7 @@ FCT (const CHAR *pattern, const CHAR *string, const CHAR *string_end, above it will be properly set by the loop. */ DIAG_PUSH_NEEDS_COMMENT; - DIAG_IGNORE_Os_NEEDS_COMMENT (8, "-Wmaybe-uninitialized"); + DIAG_IGNORE_NEEDS_COMMENT (16, "-Wmaybe-uninitialized"); cold = wextra[1 + wextra[0]]; DIAG_POP_NEEDS_COMMENT; # else @@ -745,7 +745,7 @@ FCT (const CHAR *pattern, const CHAR *string, const CHAR *string_end, means that it was properly set in the loop above. */ DIAG_PUSH_NEEDS_COMMENT; - DIAG_IGNORE_Os_NEEDS_COMMENT (8, "-Wmaybe-uninitialized"); + DIAG_IGNORE_NEEDS_COMMENT (16, "-Wmaybe-uninitialized"); cend = wextra[1 + wextra[0]]; DIAG_POP_NEEDS_COMMENT; # else @@ -754,7 +754,7 @@ FCT (const CHAR *pattern, const CHAR *string, const CHAR *string_end, wextra above it will be properly set by the loop. */ DIAG_PUSH_NEEDS_COMMENT; - DIAG_IGNORE_Os_NEEDS_COMMENT (8, "-Wmaybe-uninitialized"); + DIAG_IGNORE_NEEDS_COMMENT (16, "-Wmaybe-uninitialized"); idx += 1 + extra[idx]; DIAG_POP_NEEDS_COMMENT; /* Adjust for the alignment. */ diff --git a/posix/glob.h b/posix/glob.h index 6bb0c6f..e5442dd 100644 --- a/posix/glob.h +++ b/posix/glob.h @@ -195,7 +195,8 @@ extern void globfree64 (glob64_t *__pglob) __THROW; This function is not part of the interface specified by POSIX.2 but several programs want to use it. */ -extern int glob_pattern_p (const char *__pattern, int __quote) __THROW; +extern int glob_pattern_p (const char *__pattern, int __quote) __THROW + __nonnull ((1)); #endif __END_DECLS diff --git a/posix/regcomp.c b/posix/regcomp.c index 69675d8..f7278bb 100644 --- a/posix/regcomp.c +++ b/posix/regcomp.c @@ -3301,8 +3301,11 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, #ifdef RE_ENABLE_I18N mbcset, &coll_sym_alloc, #endif /* RE_ENABLE_I18N */ - start_elem.opr.name, - nrules, table_size, symb_table, extra); + start_elem.opr.name +#ifdef _LIBC + , nrules, table_size, symb_table, extra +#endif + ); if (__glibc_unlikely (*err != REG_NOERROR)) goto parse_bracket_exp_free_return; break; @@ -3384,6 +3387,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 +3403,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-libc-message.c b/posix/tst-libc-message.c new file mode 100644 index 0000000..b85195e --- /dev/null +++ b/posix/tst-libc-message.c @@ -0,0 +1,48 @@ +/* Internal test to verify __libc_fatal. + 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/>. */ + +#include <signal.h> +#include <stdio.h> + +#include <support/check.h> +#include <support/capture_subprocess.h> + +static _Noreturn void +run_libc_message (void *closure) +{ + /* We only support 4 arguments. Call with 5 to trigger failure. */ + __libc_message_impl ("%s %s %s %s %s\n", "1", "2", "3", "4", "5"); + __builtin_unreachable (); +} + +static int +do_test (void) +{ + struct support_capture_subprocess result + = support_capture_subprocess (run_libc_message, NULL); + support_capture_subprocess_check (&result, "libc_message", -SIGABRT, + sc_allow_stderr); + + TEST_COMPARE_STRING (result.err.buffer, IOVEC_MAX_ERR_MSG); + + support_capture_subprocess_free (&result); + + return 0; +} + +#include <support/test-driver.c> diff --git a/posix/tst-regcomp-bracket-free.c b/posix/tst-regcomp-bracket-free.c new file mode 100644 index 0000000..3c091d8 --- /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/posix/tst-regex.input b/posix/tst-regex.input index 1de30b3..38b8db6 100644 --- a/posix/tst-regex.input +++ b/posix/tst-regex.input @@ -8423,11 +8423,6 @@ * sysdeps/libm-ieee754/s_exp2f.c (__exp2f_deltatable): Renamed from __exp2_deltatable. -1998-02-26 Ulrich Drepper <drepper@cygnus.com> - - * nis/ypclnt.c (yp_master): Check result of strdup. - Patch by Thorsten Kukuk. - 1998-02-26 Thorsten Kukuk <kukuk@vt.uni-paderborn.de> * nis/ypclnt.c: Give clnt handle after error checking free, change @@ -11089,13 +11084,6 @@ inline functions by feature tests to avoid warning about missing prototype declarations. -1997-12-09 15:08 Thorsten Kukuk <kukuk@vt.uni-paderborn.de> - - * nis/nss_compat/compat-grp.c: Reset the blacklist correct, fix - return code for non existent +name entries. - * nis/nss_compat/compat-pwd.c: Likewise. - * nis/nss_compat/compat-spwd.c: Likewise. - 1997-12-10 13:52 Philip Blundell <pb@nexus.co.uk> * sysdeps/standalone/arm/bits/errno.h (EAGAIN): Added. diff --git a/posix/tst-truncate-common.c b/posix/tst-truncate-common.c index 19c96b7..20ce168 100644 --- a/posix/tst-truncate-common.c +++ b/posix/tst-truncate-common.c @@ -36,7 +36,7 @@ static int temp_fd; static void do_prepare (void) { - temp_fd = create_temp_file ("tst-trucate.", &temp_filename); + temp_fd = create_temp_file ("tst-truncate.", &temp_filename); if (temp_fd == -1) { printf ("cannot create temporary file: %m\n"); |