diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2007-01-16 19:20:54 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2007-01-16 19:20:54 +0000 |
commit | 1b3eb0a2ec9990492a2d1ac223cf5a0c95b8d378 (patch) | |
tree | 576de200f4955a3eac3230709dedf4f1e362f35d /lib/libc/stdio | |
parent | d770dd4758fea6324c1d5c62fd517a63c52b3380 (diff) |
Fix potential int overflow for printf(3) when passing in very large
values for the field width. Adapted from a diff by Christian Biere.
Diffstat (limited to 'lib/libc/stdio')
-rw-r--r-- | lib/libc/stdio/vfprintf.c | 63 |
1 files changed, 50 insertions, 13 deletions
diff --git a/lib/libc/stdio/vfprintf.c b/lib/libc/stdio/vfprintf.c index 4d482a43f57..f88ee8d04a7 100644 --- a/lib/libc/stdio/vfprintf.c +++ b/lib/libc/stdio/vfprintf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfprintf.c,v 1.40 2006/11/26 06:20:16 deraadt Exp $ */ +/* $OpenBSD: vfprintf.c,v 1.41 2007/01/16 19:20:53 millert Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. @@ -41,6 +41,7 @@ #include <sys/mman.h> #include <errno.h> +#include <limits.h> #include <stdarg.h> #include <stddef.h> #include <stdio.h> @@ -158,7 +159,7 @@ vfprintf(FILE *fp, const char *fmt0, __va_list ap) { char *fmt; /* format string */ int ch; /* character from fmt */ - int n, m, n2; /* handy integers (short term usage) */ + int n, n2; /* handy integers (short term usage) */ char *cp; /* handy char pointer (short term usage) */ struct __siov *iovp; /* for PRINT macro */ int flags; /* flags as above */ @@ -260,6 +261,18 @@ vfprintf(FILE *fp, const char *fmt0, __va_list ap) flags&CHARINT ? (unsigned char)GETARG(int) : \ GETARG(unsigned int))) + /* + * Append a digit to a value and check for overflow. + */ +#define APPEND_DIGIT(val, dig) do { \ + if ((val) > INT_MAX / 10) \ + goto overflow; \ + (val) *= 10; \ + if ((val) > INT_MAX - to_digit((dig))) \ + goto overflow; \ + (val) += to_digit((dig)); \ +} while (0) + /* * Get * arguments, including the form *nn$. Preserve the nextarg * that the argument can be gotten once the type is determined. @@ -268,7 +281,7 @@ vfprintf(FILE *fp, const char *fmt0, __va_list ap) n2 = 0; \ cp = fmt; \ while (is_digit(*cp)) { \ - n2 = 10 * n2 + to_digit(*cp); \ + APPEND_DIGIT(n2, *cp); \ cp++; \ } \ if (*cp == '$') { \ @@ -328,7 +341,10 @@ vfprintf(FILE *fp, const char *fmt0, __va_list ap) break; } } - if ((m = fmt - cp) != 0) { + if (fmt != cp) { + ptrdiff_t m = fmt - cp; + if (m < 0 || m > INT_MAX - ret) + goto overflow; PRINT(cp, m); ret += m; } @@ -366,6 +382,8 @@ reswitch: switch (ch) { GETASTER(width); if (width >= 0) goto rflag; + if (width == INT_MIN) + goto overflow; width = -width; /* FALLTHROUGH */ case '-': @@ -382,7 +400,7 @@ reswitch: switch (ch) { } n = 0; while (is_digit(ch)) { - n = 10 * n + to_digit(ch); + APPEND_DIGIT(n, ch); ch = *fmt++; } if (ch == '$') { @@ -394,7 +412,7 @@ reswitch: switch (ch) { } goto rflag; } - prec = n < 0 ? -1 : n; + prec = n; goto reswitch; case '0': /* @@ -408,7 +426,7 @@ reswitch: switch (ch) { case '5': case '6': case '7': case '8': case '9': n = 0; do { - n = 10 * n + to_digit(ch); + APPEND_DIGIT(n, ch); ch = *fmt++; } while (is_digit(ch)); if (ch == '$') { @@ -780,13 +798,26 @@ number: if ((dprec = prec) >= 0) PAD(width - realsz, blanks); /* finally, adjust ret */ - ret += width > realsz ? width : realsz; + if (width < realsz) + width = realsz; + if (width > INT_MAX - ret) + goto overflow; + ret += width; FLUSH(); /* copy out the I/O vectors */ } done: FLUSH(); error: + if (__sferror(fp)) + ret = -1; + goto finish; + +overflow: + errno = ENOMEM; + ret = -1; + +finish: #ifdef FLOATING_POINT if (dtoaresult) __freedtoa(dtoaresult); @@ -795,8 +826,7 @@ error: munmap(argtable, argtablesiz); argtable = NULL; } - return (__sferror(fp) ? EOF : ret); - /* NOTREACHED */ + return (ret); } /* @@ -850,6 +880,7 @@ __find_arguments(const char *fmt0, va_list ap, va_list **argtable, int tablesize; /* current size of type table */ int tablemax; /* largest used index in table */ int nextarg; /* 1-based argument index */ + int ret = 0; /* return value */ wchar_t wc; mbstate_t ps; @@ -885,7 +916,7 @@ __find_arguments(const char *fmt0, va_list ap, va_list **argtable, n2 = 0; \ cp = fmt; \ while (is_digit(*cp)) { \ - n2 = 10 * n2 + to_digit(*cp); \ + APPEND_DIGIT(n2, *cp); \ cp++; \ } \ if (*cp == '$') { \ @@ -949,7 +980,7 @@ reswitch: switch (ch) { case '5': case '6': case '7': case '8': case '9': n = 0; do { - n = 10 * n + to_digit(ch); + APPEND_DIGIT(n ,ch); ch = *fmt++; } while (is_digit(ch)); if (ch == '$') { @@ -1141,12 +1172,18 @@ done: break; } } + goto finish; + +overflow: + errno = ENOMEM; + ret = -1; +finish: if (typetable != NULL && typetable != stattypetable) { munmap(typetable, *argtablesiz); typetable = NULL; } - return (0); + return (ret); } /* |