summaryrefslogtreecommitdiff
path: root/stdio-common/vfprintf.c
diff options
context:
space:
mode:
authorJeff Law <law@redhat.com>2014-12-15 10:09:32 +0100
committerFlorian Weimer <fweimer@redhat.com>2014-12-15 10:09:33 +0100
commita5357b7ce2a2982c5778435704bcdb55ce3667a0 (patch)
tree1292d6cb3f935bf84f07a1acc2fc92409dce1084 /stdio-common/vfprintf.c
parent3a12c70f137707074209241e6c6172ea25f9ab4a (diff)
downloadglibc-a5357b7ce2a2982c5778435704bcdb55ce3667a0.tar.gz
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.c40
1 files changed, 38 insertions, 2 deletions
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index c4ff8334b2..429a3d1a83 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))