tst-assert-sa-2025-0001.c 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /* Test for CVE-2025-0395.
  2. Copyright The GNU Toolchain Authors.
  3. This file is part of the GNU C Library.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, see
  14. <https://www.gnu.org/licenses/>. */
  15. /* Test that a large enough __progname does not result in a buffer overflow
  16. when printing an assertion failure. This was CVE-2025-0395. */
  17. #include <assert.h>
  18. #include <inttypes.h>
  19. #include <signal.h>
  20. #include <stdbool.h>
  21. #include <string.h>
  22. #include <sys/mman.h>
  23. #include <support/check.h>
  24. #include <support/support.h>
  25. #include <support/xstdio.h>
  26. #include <support/xunistd.h>
  27. extern const char *__progname;
  28. int
  29. do_test (int argc, char **argv)
  30. {
  31. support_need_proc ("Reads /proc/self/maps to add guards to writable maps.");
  32. ignore_stderr ();
  33. /* XXX assumes that the assert is on a 2 digit line number. */
  34. const char *prompt = ": %s:99: do_test: Assertion `argc < 1' failed.\n";
  35. int ret = fprintf (stderr, prompt, __FILE__);
  36. if (ret < 0)
  37. FAIL_EXIT1 ("fprintf failed: %m\n");
  38. size_t pagesize = getpagesize ();
  39. size_t namesize = pagesize - 1 - ret;
  40. /* Alter the progname so that the assert message fills the entire page. */
  41. char progname[namesize];
  42. memset (progname, 'A', namesize - 1);
  43. progname[namesize - 1] = '\0';
  44. __progname = progname;
  45. FILE *f = xfopen ("/proc/self/maps", "r");
  46. char *line = NULL;
  47. size_t len = 0;
  48. uintptr_t prev_to = 0;
  49. /* Pad the beginning of every writable mapping with a PROT_NONE map. This
  50. ensures that the mmap in the assert_fail path never ends up below a
  51. writable map and will terminate immediately in case of a buffer
  52. overflow. */
  53. while (xgetline (&line, &len, f))
  54. {
  55. uintptr_t from, to;
  56. char perm[4];
  57. sscanf (line, "%" SCNxPTR "-%" SCNxPTR " %c%c%c%c ",
  58. &from, &to,
  59. &perm[0], &perm[1], &perm[2], &perm[3]);
  60. bool writable = (memchr (perm, 'w', 4) != NULL);
  61. if (prev_to != 0 && from - prev_to > pagesize && writable)
  62. xmmap ((void *) from - pagesize, pagesize, PROT_NONE,
  63. MAP_ANONYMOUS | MAP_PRIVATE, 0);
  64. prev_to = to;
  65. }
  66. xfclose (f);
  67. assert (argc < 1);
  68. return 0;
  69. }
  70. #define EXPECTED_SIGNAL SIGABRT
  71. #define TEST_FUNCTION_ARGV do_test
  72. #include <support/test-driver.c>