diff options
author | Jeff Law <law@redhat.com> | 2014-12-15 10:09:32 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2014-12-15 10:09:33 +0100 |
commit | a5357b7ce2a2982c5778435704bcdb55ce3667a0 (patch) | |
tree | 1292d6cb3f935bf84f07a1acc2fc92409dce1084 /stdio-common/vfprintf.c | |
parent | 3a12c70f137707074209241e6c6172ea25f9ab4a (diff) | |
download | glibc-a5357b7ce2a2982c5778435704bcdb55ce3667a0.zip glibc-a5357b7ce2a2982c5778435704bcdb55ce3667a0.tar.gz glibc-a5357b7ce2a2982c5778435704bcdb55ce3667a0.tar.bz2 |
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.
Diffstat (limited to 'stdio-common/vfprintf.c')
-rw-r--r-- | stdio-common/vfprintf.c | 40 |
1 files changed, 38 insertions, 2 deletions
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c index c4ff833..429a3d1 100644 --- a/stdio-common/vfprintf.c +++ b/stdio-common/vfprintf.c @@ -263,6 +263,12 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) /* 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. */ @@ -1679,8 +1685,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. */ @@ -1721,11 +1727,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. */ @@ -2046,6 +2080,8 @@ do_positional: } all_done: + if (specs_malloced) + free (specs); if (__glibc_unlikely (args_malloced != NULL)) free (args_malloced); if (__glibc_unlikely (workstart != NULL)) |