headers := assert.h
routines := assert assert-perr __assert
-tests := test-assert test-assert-perr tst-assert-c++ tst-assert-g++
+tests := test-assert test-assert-perr tst-assert-c++ tst-assert-g++ tst-assert-sa-2025-0001
ifeq ($(have-cxx-thread_local),yes)
CFLAGS-tst-assert-c++.o = -std=c++11
--- /dev/null
+/* Test for CVE-2025-0395.
+ Copyright The GNU Toolchain Authors.
+ 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/>. */
+
+/* Test that a large enough __progname does not result in a buffer overflow
+ when printing an assertion failure. This was CVE-2025-0395. */
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+extern const char *__progname;
+
+int
+do_test (int argc, char **argv)
+{
+
+ support_need_proc ("Reads /proc/self/maps to add guards to writable maps.");
+ ignore_stderr ();
+
+ /* XXX assumes that the assert is on a 2 digit line number. */
+ const char *prompt = ": %s:99: do_test: Assertion `argc < 1' failed.\n";
+
+ int ret = fprintf (stderr, prompt, __FILE__);
+ if (ret < 0)
+ FAIL_EXIT1 ("fprintf failed: %m\n");
+
+ size_t pagesize = getpagesize ();
+ size_t namesize = pagesize - 1 - ret;
+
+ /* Alter the progname so that the assert message fills the entire page. */
+ char progname[namesize];
+ memset (progname, 'A', namesize - 1);
+ progname[namesize - 1] = '\0';
+ __progname = progname;
+
+ FILE *f = xfopen ("/proc/self/maps", "r");
+ char *line = NULL;
+ size_t len = 0;
+ uintptr_t prev_to = 0;
+
+ /* Pad the beginning of every writable mapping with a PROT_NONE map. This
+ ensures that the mmap in the assert_fail path never ends up below a
+ writable map and will terminate immediately in case of a buffer
+ overflow. */
+ while (xgetline (&line, &len, f))
+ {
+ uintptr_t from, to;
+ char perm[4];
+
+ sscanf (line, "%" SCNxPTR "-%" SCNxPTR " %c%c%c%c ",
+ &from, &to,
+ &perm[0], &perm[1], &perm[2], &perm[3]);
+
+ bool writable = (memchr (perm, 'w', 4) != NULL);
+
+ if (prev_to != 0 && from - prev_to > pagesize && writable)
+ xmmap ((void *) from - pagesize, pagesize, PROT_NONE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0);
+
+ prev_to = to;
+ }
+
+ xfclose (f);
+
+ assert (argc < 1);
+ return 0;
+}
+
+#define EXPECTED_SIGNAL SIGABRT
+#define TEST_FUNCTION_ARGV do_test
+#include <support/test-driver.c>
support_format_hostent \
support_format_netent \
support_isolate_in_subprocess \
+ support_need_proc \
support_ptrace \
support_openpty \
support_paths \
xfork \
xftruncate \
xgetsockname \
+ xgetline \
xlisten \
xlseek \
xmalloc \
regular file open for writing, and initially empty. */
int support_descriptor_supports_holes (int fd);
+/* Predicates that a test requires a working /proc filesystem. This
+ call will exit with UNSUPPORTED if /proc is not available, printing
+ WHY_MSG as part of the diagnostic. */
+void support_need_proc (const char *why_msg);
+
/* Error-checking wrapper functions which terminate the process on
error. */
--- /dev/null
+/* Indicate that a test requires a working /proc.
+ Copyright (C) 2022 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 <unistd.h>
+#include <support/check.h>
+#include <support/support.h>
+
+/* We test for /proc/self/maps since that's one of the files that one
+ of our tests actually uses, but the general idea is if Linux's
+ /proc/ (procfs) filesystem is mounted. If not, the process exits
+ with an UNSUPPORTED result code. */
+
+void
+support_need_proc (const char *why_msg)
+{
+#ifdef __linux__
+ if (access ("/proc/self/maps", R_OK))
+ FAIL_UNSUPPORTED ("/proc is not available, %s", why_msg);
+#endif
+}
--- /dev/null
+/* fopen with error checking.
+ Copyright (C) 2020-2021 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 <support/xstdio.h>
+#include <support/check.h>
+
+size_t
+xgetline (char **lineptr, size_t *n, FILE *stream)
+{
+ TEST_VERIFY (!ferror (stream));
+ ssize_t ret = getline (lineptr, n, stream);
+ if (ferror (stream))
+ {
+ TEST_VERIFY (ret < 0);
+ FAIL_EXIT1 ("getline: %m");
+ }
+ if (feof (stream))
+ {
+ TEST_VERIFY (ret <= 0);
+ return 0;
+ }
+ TEST_VERIFY (ret > 0);
+ return ret;
+}
FILE *xfopen (const char *path, const char *mode);
void xfclose (FILE *);
+/* Read a line from FP, using getline. *BUFFER must be NULL, or a
+ heap-allocated pointer of *LENGTH bytes. Return the number of
+ bytes in the line if a line was read, or 0 on EOF. */
+size_t xgetline (char **lineptr, size_t *n, FILE *stream);
+
__END_DECLS
#endif /* SUPPORT_XSTDIO_H */