Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/lib/libc/stdio Fix: CVE-2012-0864 fprintf() positional argum...
details: https://anonhg.NetBSD.org/src/rev/da17397e05d0
branches: trunk
changeset: 777413:da17397e05d0
user: christos <christos%NetBSD.org@localhost>
date: Fri Feb 17 19:57:53 2012 +0000
description:
Fix: CVE-2012-0864 fprintf() positional argument abuse.
Described in: http://www.phrack.org/issues.html?issue=67&id=9#article
Reported by Stefan Cornelius / Red Hat Security Response Team
- convert internal positional arguments bookkeeping from int to size_t
- provide overflow protection in positional argument spec
- convert loops to memset
- fix memory leaks
- limit positional argument stack offset to the number of arguments required
by the printf to avoid coredump from va_arg() exhaustion.
diffstat:
lib/libc/stdio/vfwprintf.c | 68 +++++++++++++++++++++++++++++++--------------
1 files changed, 46 insertions(+), 22 deletions(-)
diffs (178 lines):
diff -r 320b4ae8c3f7 -r da17397e05d0 lib/libc/stdio/vfwprintf.c
--- a/lib/libc/stdio/vfwprintf.c Fri Feb 17 19:00:45 2012 +0000
+++ b/lib/libc/stdio/vfwprintf.c Fri Feb 17 19:57:53 2012 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: vfwprintf.c,v 1.24 2011/08/17 09:53:54 christos Exp $ */
+/* $NetBSD: vfwprintf.c,v 1.25 2012/02/17 19:57:53 christos Exp $ */
/*-
* Copyright (c) 1990, 1993
@@ -38,7 +38,7 @@
static char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) 6/4/93";
__FBSDID("$FreeBSD: src/lib/libc/stdio/vfwprintf.c,v 1.27 2007/01/09 00:28:08 imp Exp $");
#else
-__RCSID("$NetBSD: vfwprintf.c,v 1.24 2011/08/17 09:53:54 christos Exp $");
+__RCSID("$NetBSD: vfwprintf.c,v 1.25 2012/02/17 19:57:53 christos Exp $");
#endif
#endif /* LIBC_SCCS and not lint */
@@ -124,7 +124,7 @@
* Type ids for argument type table.
*/
enum typeid {
- T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT,
+ T_UNUSED = 0, TP_SHORT, T_INT, T_U_INT, TP_INT,
T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG,
T_PTRDIFFT, TP_PTRDIFFT, T_SSIZET, T_SIZET, TP_SIZET,
T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR,
@@ -144,7 +144,7 @@
static int __sprint(FILE *, struct __suio *);
#endif
static int __find_arguments(const CHAR_T *, va_list, union arg **);
-static int __grow_type_table(int, enum typeid **, int *);
+static int __grow_type_table(size_t, enum typeid **, size_t *);
/*
* Helper function for `fprintf to unbuffered unix file': creates a
@@ -1554,20 +1554,27 @@
{
CHAR_T *fmt; /* format string */
int ch; /* character from fmt */
- int n, n2; /* handy integer (short term usage) */
+ size_t n, n2; /* handy index (short term usage) */
CHAR_T *cp; /* handy char pointer (short term usage) */
int flags; /* flags as above */
enum typeid *typetable; /* table of types */
enum typeid stattypetable [STATIC_ARG_TBL_SIZE];
- int tablesize; /* current size of type table */
- int tablemax; /* largest used index in table */
- int nextarg; /* 1-based argument index */
+ size_t tablesize; /* current size of type table */
+ size_t tablemax; /* largest used index in table */
+ size_t nextarg; /* 1-based argument index */
+ size_t nitems; /* number of items we picked from the stack */
/*
* Add an argument type to the table, expanding if necessary.
+ * Check for overflow.
*/
#define ADDTYPE(type) \
do { \
+ if (nextarg > SIZE_MAX / sizeof(**argtable)) { \
+ if (typetable != stattypetable) \
+ free(typetable); \
+ return -1; \
+ } \
if (nextarg >= tablesize) \
if (__grow_type_table(nextarg, &typetable, \
&tablesize) == -1) \
@@ -1575,6 +1582,7 @@
if (nextarg > tablemax) \
tablemax = nextarg; \
typetable[nextarg++] = type; \
+ nitems++; \
} while (/*CONSTCOND*/0)
#define ADDSARG() \
@@ -1619,7 +1627,7 @@
cp++; \
} \
if (*cp == '$') { \
- int hold = nextarg; \
+ size_t hold = nextarg; \
nextarg = n2; \
ADDTYPE(T_INT); \
nextarg = hold; \
@@ -1628,12 +1636,12 @@
ADDTYPE(T_INT); \
}
fmt = (CHAR_T *)__UNCONST(fmt0);
+ memset(stattypetable, 0, sizeof(stattypetable));
typetable = stattypetable;
tablesize = STATIC_ARG_TBL_SIZE;
tablemax = 0;
nextarg = 1;
- for (n = 0; n < STATIC_ARG_TBL_SIZE; n++)
- typetable[n] = T_UNUSED;
+ nitems = 1;
/*
* Scan the format for conversions (`%' character).
@@ -1795,13 +1803,30 @@
}
done:
/*
+ * nitems contains the number of arguments we picked from the stack.
+ * If tablemax is larger, this means that some positional argument,
+ * tried to pick an argument the number of arguments possibly supplied.
+ * Since positional arguments are typically used to swap the order of
+ * the printf arguments and not to pick random arguments from strange
+ * positions in the stack, we assume that if the positional argument
+ * is trying to pick beyond the end of arguments, then this is wrong.
+ * Alternatively we could find a way to figure out when va_arg() runs
+ * out, but how to do that?
+ */
+ if (nitems < tablemax) {
+ if (typetable != stattypetable)
+ free(typetable);
+ return -1;
+ }
+ /*
* Build the argument table.
*/
if (tablemax >= STATIC_ARG_TBL_SIZE) {
- *argtable = (union arg *)
- malloc (sizeof (union arg) * (tablemax + 1));
- if (*argtable == NULL)
+ *argtable = malloc(sizeof(**argtable) * (tablemax + 1));
+ if (*argtable == NULL) {
+ free(typetable);
return -1;
+ }
}
(*argtable) [0].intarg = 0;
@@ -1892,7 +1917,7 @@
}
}
- if ((typetable != NULL) && (typetable != stattypetable))
+ if (typetable != stattypetable)
free (typetable);
return 0;
}
@@ -1901,28 +1926,27 @@
* Increase the size of the type table.
*/
static int
-__grow_type_table (int nextarg, enum typeid **typetable, int *tablesize)
+__grow_type_table (size_t nextarg, enum typeid **typetable, size_t *tablesize)
{
enum typeid *const oldtable = *typetable;
const int oldsize = *tablesize;
enum typeid *newtable;
- int n, newsize = oldsize * 2;
+ size_t n, newsize = oldsize * 2;
if (newsize < nextarg + 1)
newsize = nextarg + 1;
if (oldsize == STATIC_ARG_TBL_SIZE) {
- if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL)
+ if ((newtable = malloc(newsize * sizeof(*newtable))) == NULL)
return -1;
- memcpy(newtable, oldtable, oldsize * sizeof(enum typeid));
+ memcpy(newtable, oldtable, oldsize * sizeof(*newtable));
} else {
- newtable = realloc(oldtable, newsize * sizeof(enum typeid));
+ newtable = realloc(oldtable, newsize * sizeof(*newtable));
if (newtable == NULL) {
free(oldtable);
return -1;
}
}
- for (n = oldsize; n < newsize; n++)
- newtable[n] = T_UNUSED;
+ memset(&newtable[oldsize], 0, (newsize - oldsize) * sizeof(*newtable));
*typetable = newtable;
*tablesize = newsize;
Home |
Main Index |
Thread Index |
Old Index