From a5357b7ce2a2982c5778435704bcdb55ce3667a0 Mon Sep 17 00:00:00 2001 From: Jeff Law Date: Mon, 15 Dec 2014 10:09:32 +0100 Subject: [PATCH] CVE-2012-3406: Stack overflow in vfprintf [BZ #16617] A larger number of format specifiers coudld cause a stack overflow, potentially allowing to bypass _FORTIFY_SOURCE format string protection. --- ChangeLog | 9 +++++++ NEWS | 13 +++++---- stdio-common/Makefile | 2 +- stdio-common/bug23-2.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ stdio-common/bug23-3.c | 50 +++++++++++++++++++++++++++++++++++ stdio-common/bug23-4.c | 31 ++++++++++++++++++++++ stdio-common/vfprintf.c | 40 ++++++++++++++++++++++++++-- 7 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 stdio-common/bug23-2.c create mode 100644 stdio-common/bug23-3.c create mode 100644 stdio-common/bug23-4.c Index: git/ChangeLog =================================================================== --- git.orig/ChangeLog +++ git/ChangeLog @@ -1,3 +1,12 @@ +2014-12-15 Jeff Law + + [BZ #16617] + * stdio-common/vfprintf.c (vfprintf): Allocate large specs array + on the heap. (CVE-2012-3406) + * stdio-common/bug23-2.c, stdio-common/bug23-3.c: New file. + * stdio-common/bug23-4.c: New file. Test case by Joseph Myers. + * stdio-common/Makefile (tests): Add bug23-2, bug23-3, bug23-4. + 2014-11-19 Carlos O'Donell Florian Weimer Joseph Myers Index: git/NEWS =================================================================== --- git.orig/NEWS +++ git/NEWS @@ -13,24 +13,28 @@ Version 2.20 15698, 15804, 15894, 15946, 16002, 16064, 16095, 16194, 16198, 16275, 16284, 16287, 16315, 16348, 16349, 16354, 16357, 16362, 16447, 16516, 16532, 16539, 16545, 16561, 16562, 16564, 16574, 16599, 16600, 16609, - 16610, 16611, 16613, 16619, 16623, 16629, 16632, 16634, 16639, 16642, - 16648, 16649, 16670, 16674, 16677, 16680, 16681, 16683, 16689, 16695, - 16701, 16706, 16707, 16712, 16713, 16714, 16724, 16731, 16739, 16740, - 16743, 16754, 16758, 16759, 16760, 16770, 16786, 16789, 16791, 16796, - 16799, 16800, 16815, 16823, 16824, 16831, 16838, 16839, 16849, 16854, - 16876, 16877, 16878, 16882, 16885, 16888, 16890, 16892, 16912, 16915, - 16916, 16917, 16918, 16922, 16927, 16928, 16932, 16943, 16958, 16965, - 16966, 16967, 16977, 16978, 16984, 16990, 16996, 17009, 17022, 17031, - 17042, 17048, 17050, 17058, 17061, 17062, 17069, 17075, 17078, 17079, - 17084, 17086, 17088, 17092, 17097, 17125, 17135, 17137, 17150, 17153, - 17187, 17213, 17259, 17261, 17262, 17263, 17319, 17325, 17354, 17625. - + 16610, 16611, 16613, 16617, 16619, 16623, 16629, 16632, 16634, 16639, + 16642, 16648, 16649, 16670, 16674, 16677, 16680, 16681, 16683, 16689, + 16695, 16701, 16706, 16707, 16712, 16713, 16714, 16724, 16731, 16739, + 16740, 16743, 16754, 16758, 16759, 16760, 16770, 16786, 16789, 16791, + 16796, 16799, 16800, 16815, 16823, 16824, 16831, 16838, 16839, 16849, + 16854, 16876, 16877, 16878, 16882, 16885, 16888, 16890, 16892, 16912, + 16915, 16916, 16917, 16918, 16922, 16927, 16928, 16932, 16943, 16958, + 16965, 16966, 16967, 16977, 16978, 16984, 16990, 16996, 17009, 17022, + 17031, 17042, 17048, 17050, 17058, 17061, 17062, 17069, 17075, 17078, + 17079, 17084, 17086, 17088, 17092, 17097, 17125, 17135, 17137, 17150, + 17153, 17187, 17213, 17259, 17261, 17262, 17263, 17319, 17325, 17354, + 17625. + * CVE-2104-7817 The wordexp function could ignore the WRDE_NOCMD flag under certain input conditions resulting in the execution of a shell for command substitution when the applicaiton did not request it. The implementation now checks WRDE_NOCMD immediately before executing the shell and returns the error WRDE_CMDSUB as expected. +* CVE-2012-3406 printf-style functions could run into a stack overflow when + processing format strings with a large number of format specifiers. + * Reverted change of ABI data structures for s390 and s390x: On s390 and s390x the size of struct ucontext and jmp_buf was increased in 2.19. This change is reverted in 2.20. The introduced 2.19 symbol versions Index: git/stdio-common/bug23-2.c =================================================================== --- /dev/null +++ git/stdio-common/bug23-2.c @@ -0,0 +1,70 @@ +#include +#include +#include + +static const char expected[] = "\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55\ +\n\ +a\n\ +abbcd55%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + +static int +do_test (void) +{ + char *buf = malloc (strlen (expected) + 1); + snprintf (buf, strlen (expected) + 1, + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + "a", "b", "c", "d", 5); + return strcmp (buf, expected) != 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" Index: git/stdio-common/bug23-3.c =================================================================== --- /dev/null +++ git/stdio-common/bug23-3.c @@ -0,0 +1,50 @@ +#include +#include +#include + +int +do_test (void) +{ + size_t instances = 16384; +#define X0 "\n%1$s\n" "%1$s" "%2$s" "%2$s" "%3$s" "%4$s" "%5$d" "%5$d" + const char *item = "\na\nabbcd55"; +#define X3 X0 X0 X0 X0 X0 X0 X0 X0 +#define X6 X3 X3 X3 X3 X3 X3 X3 X3 +#define X9 X6 X6 X6 X6 X6 X6 X6 X6 +#define X12 X9 X9 X9 X9 X9 X9 X9 X9 +#define X14 X12 X12 X12 X12 +#define TRAILER "%%%%%%%%%%%%%%%%%%%%%%%%%%" +#define TRAILER2 TRAILER TRAILER + size_t length = instances * strlen (item) + strlen (TRAILER) + 1; + + char *buf = malloc (length + 1); + snprintf (buf, length + 1, + X14 TRAILER2 "\n", + "a", "b", "c", "d", 5); + + const char *p = buf; + size_t i; + for (i = 0; i < instances; ++i) + { + const char *expected; + for (expected = item; *expected; ++expected) + { + if (*p != *expected) + { + printf ("mismatch at offset %zu (%zu): expected %d, got %d\n", + (size_t) (p - buf), i, *expected & 0xFF, *p & 0xFF); + return 1; + } + ++p; + } + } + if (strcmp (p, TRAILER "\n") != 0) + { + printf ("mismatch at trailer: [%s]\n", p); + return 1; + } + free (buf); + return 0; +} +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" Index: git/stdio-common/bug23-4.c =================================================================== --- /dev/null +++ git/stdio-common/bug23-4.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +#define LIMIT 1000000 + +int +main (void) +{ + struct rlimit lim; + getrlimit (RLIMIT_STACK, &lim); + lim.rlim_cur = 1048576; + setrlimit (RLIMIT_STACK, &lim); + char *fmtstr = malloc (4 * LIMIT + 1); + if (fmtstr == NULL) + abort (); + char *output = malloc (LIMIT + 1); + if (output == NULL) + abort (); + for (size_t i = 0; i < LIMIT; i++) + memcpy (fmtstr + 4 * i, "%1$d", 4); + fmtstr[4 * LIMIT] = '\0'; + int ret = snprintf (output, LIMIT + 1, fmtstr, 0); + if (ret != LIMIT) + abort (); + for (size_t i = 0; i < LIMIT; i++) + if (output[i] != '0') + abort (); + return 0; +} Index: git/stdio-common/vfprintf.c =================================================================== --- git.orig/stdio-common/vfprintf.c +++ git/stdio-common/vfprintf.c @@ -276,6 +276,12 @@ vfprintf (FILE *s, const CHAR_T *format, /* For the argument descriptions, which may be allocated on the heap. */ void *args_malloced = NULL; + /* For positional argument handling. */ + struct printf_spec *specs; + + /* Track if we malloced the SPECS array and thus must free it. */ + bool specs_malloced = false; + /* This table maps a character into a number representing a class. In each step there is a destination label for each class. */ @@ -1699,8 +1705,8 @@ do_positional: size_t nspecs = 0; /* A more or less arbitrary start value. */ size_t nspecs_size = 32 * sizeof (struct printf_spec); - struct printf_spec *specs = alloca (nspecs_size); + specs = alloca (nspecs_size); /* The number of arguments the format string requests. This will determine the size of the array needed to store the argument attributes. */ @@ -1743,11 +1749,39 @@ do_positional: if (nspecs * sizeof (*specs) >= nspecs_size) { /* Extend the array of format specifiers. */ + if (nspecs_size * 2 < nspecs_size) + { + __set_errno (ENOMEM); + done = -1; + goto all_done; + } struct printf_spec *old = specs; - specs = extend_alloca (specs, nspecs_size, 2 * nspecs_size); + if (__libc_use_alloca (2 * nspecs_size)) + specs = extend_alloca (specs, nspecs_size, 2 * nspecs_size); + else + { + nspecs_size *= 2; + specs = malloc (nspecs_size); + if (specs == NULL) + { + __set_errno (ENOMEM); + specs = old; + done = -1; + goto all_done; + } + } /* Copy the old array's elements to the new space. */ memmove (specs, old, nspecs * sizeof (*specs)); + + /* If we had previously malloc'd space for SPECS, then + release it after the copy is complete. */ + if (specs_malloced) + free (old); + + /* Now set SPECS_MALLOCED if needed. */ + if (!__libc_use_alloca (nspecs_size)) + specs_malloced = true; } /* Parse the format specifier. */ @@ -2068,6 +2102,8 @@ do_positional: } all_done: + if (specs_malloced) + free (specs); if (__glibc_unlikely (args_malloced != NULL)) free (args_malloced); if (__glibc_unlikely (workstart != NULL)) Index: git/stdio-common/Makefile =================================================================== --- git.orig/stdio-common/Makefile +++ git/stdio-common/Makefile @@ -66,7 +66,7 @@ tests := tstscanf test_rdwr test-popen t tst-fwrite bug16 bug17 tst-sprintf2 bug18 \ bug19 tst-popen2 scanf14 scanf15 bug21 bug22 scanf16 scanf17 \ tst-setvbuf1 bug23 bug24 bug-vfprintf-nargs tst-sprintf3 bug25 \ - tst-printf-round bug26 + tst-printf-round bug23-2 bug23-3 bug23-4 tests-$(OPTION_EGLIBC_LOCALE_CODE) \ += tst-sscanf tst-swprintf test-vfprintf bug14 scanf13 tst-grouping