1 From 167dfe9672c116b315e72e57a55c7769f180dffa Mon Sep 17 00:00:00 2001
2 From: Rich Felker <dalias@aerifal.cx>
3 Date: Thu, 20 Oct 2016 00:22:09 -0400
4 Subject: fix integer overflows and uncaught EOVERFLOW in printf core
6 this patch fixes a large number of missed internal signed-overflow
7 checks and errors in determining when the return value (output length)
8 would exceed INT_MAX, which should result in EOVERFLOW. some of the
9 issues fixed were reported by Alexander Cherepanov; others were found
10 in subsequent review of the code.
12 aside from the signed overflows being undefined behavior, the
13 following specific bugs were found to exist in practice:
15 - overflows computing length of floating point formats with huge
16 explicit precisions, integer formats with prefix characters and huge
17 explicit precisions, or string arguments or format strings longer
18 than INT_MAX, resulted in wrong return value and wrong %n results.
20 - literal width and precision values outside the range of int were
21 misinterpreted, yielding wrong behavior in at least one well-defined
22 case: string formats with precision greater than INT_MAX were
25 - in cases where EOVERFLOW is produced, incorrect values could be
26 written for %n specifiers past the point of exceeding INT_MAX.
28 in addition to fixing these bugs, we now stop producing output
29 immediately when output length would exceed INT_MAX, rather than
30 continuing and returning an error only at the end.
32 src/stdio/vfprintf.c | 72 +++++++++++++++++++++++++++++++++++----------------
33 src/stdio/vfwprintf.c | 63 +++++++++++++++++++++++++++-----------------
34 2 files changed, 89 insertions(+), 46 deletions(-)
36 diff --git a/src/stdio/vfprintf.c b/src/stdio/vfprintf.c
37 index cd17ad7..e2ab2dc 100644
38 --- a/src/stdio/vfprintf.c
39 +++ b/src/stdio/vfprintf.c
40 @@ -272,6 +272,8 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t)
41 if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
44 + if (p > INT_MAX-2-(ebuf-estr)-pl)
47 l = (p+2) + (ebuf-estr);
49 @@ -383,17 +385,22 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t)
50 p = MIN(p,MAX(0,9*(z-r-1)+e-j));
53 + if (p > INT_MAX-1-(p || (fl&ALT_FORM)))
55 l = 1 + p + (p || (fl&ALT_FORM));
57 + if (e > INT_MAX-l) return -1;
60 estr=fmt_u(e<0 ? -e : e, ebuf);
61 while(ebuf-estr<2) *--estr='0';
62 *--estr = (e<0 ? '-' : '+');
64 + if (ebuf-estr > INT_MAX-l) return -1;
68 + if (l > INT_MAX-pl) return -1;
69 pad(f, ' ', w, pl+l, fl);
71 pad(f, '0', w, pl+l, fl^ZERO_PAD);
72 @@ -437,8 +444,10 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t)
74 static int getint(char **s) {
76 - for (i=0; isdigit(**s); (*s)++)
77 - i = 10*i + (**s-'0');
78 + for (i=0; isdigit(**s); (*s)++) {
79 + if (i > INT_MAX/10U || **s-'0' > INT_MAX-10*i) i = -1;
80 + else i = 10*i + (**s-'0');
85 @@ -446,12 +455,12 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg,
87 char *a, *z, *s=(char *)fmt;
97 char buf[sizeof(uintmax_t)*3+3+LDBL_MANT_DIG/4];
100 @@ -459,18 +468,19 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg,
104 + /* This error is only specified for snprintf, but since it's
105 + * unspecified for other forms, do the same. Stop immediately
106 + * on overflow; otherwise %n could produce wrong results. */
107 + if (l > INT_MAX - cnt) goto overflow;
109 /* Update output count, end loop when fmt is exhausted */
111 - if (l > INT_MAX - cnt) {
119 /* Handle literal text and %% format specifiers */
120 for (a=s; *s && *s!='%'; s++);
121 for (z=s; s[0]=='%' && s[1]=='%'; z++, s+=2);
122 + if (z-a > INT_MAX-cnt) goto overflow;
126 @@ -498,9 +508,9 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg,
128 w = f ? va_arg(*ap, int) : 0;
132 if (w<0) fl|=LEFT_ADJ, w=-w;
133 - } else if ((w=getint(&s))<0) return -1;
134 + } else if ((w=getint(&s))<0) goto overflow;
137 if (*s=='.' && s[1]=='*') {
138 @@ -511,24 +521,29 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg,
140 p = f ? va_arg(*ap, int) : 0;
145 } else if (*s=='.') {
155 /* Format specifier state machine */
158 - if (OOB(*s)) return -1;
159 + if (OOB(*s)) goto inval;
161 st=states[st]S(*s++);
163 - if (!st) return -1;
164 + if (!st) goto inval;
166 /* Check validity of argument type (nl/normal) */
168 - if (argpos>=0) return -1;
169 + if (argpos>=0) goto inval;
171 if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos];
172 else if (f) pop_arg(&arg, st, ap);
173 @@ -584,6 +599,7 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg,
177 + if (xp && p<0) goto overflow;
178 if (p>=0) fl &= ~ZERO_PAD;
181 @@ -599,9 +615,9 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg,
182 if (1) a = strerror(errno); else
184 a = arg.p ? arg.p : "(null)";
185 - z = memchr(a, 0, p);
188 + z = a + strnlen(a, p<0 ? INT_MAX : p);
189 + if (p<0 && *z) goto overflow;
194 @@ -611,8 +627,9 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg,
198 - for (i=l=0; i<0U+p && *ws && (l=wctomb(mb, *ws++))>=0 && l<=0U+p-i; i+=l);
199 + for (i=l=0; i<p && *ws && (l=wctomb(mb, *ws++))>=0 && l<=p-i; i+=l);
201 + if (i > INT_MAX) goto overflow;
203 pad(f, ' ', w, p, fl);
205 @@ -623,12 +640,16 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg,
207 case 'e': case 'f': case 'g': case 'a':
208 case 'E': case 'F': case 'G': case 'A':
209 + if (xp && p<0) goto overflow;
210 l = fmt_fp(f, arg.f, w, p, fl, t);
211 + if (l<0) goto overflow;
215 if (p < z-a) p = z-a;
216 + if (p > INT_MAX-pl) goto overflow;
217 if (w < pl+p) w = pl+p;
218 + if (w > INT_MAX-cnt) goto overflow;
220 pad(f, ' ', w, pl+p, fl);
222 @@ -646,8 +667,15 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg,
223 for (i=1; i<=NL_ARGMAX && nl_type[i]; i++)
224 pop_arg(nl_arg+i, nl_type[i], ap);
225 for (; i<=NL_ARGMAX && !nl_type[i]; i++);
226 - if (i<=NL_ARGMAX) return -1;
227 + if (i<=NL_ARGMAX) goto inval;
238 int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap)
239 diff --git a/src/stdio/vfwprintf.c b/src/stdio/vfwprintf.c
240 index f9f1ecf..b8fff20 100644
241 --- a/src/stdio/vfwprintf.c
242 +++ b/src/stdio/vfwprintf.c
243 @@ -154,8 +154,10 @@ static void out(FILE *f, const wchar_t *s, size_t l)
245 static int getint(wchar_t **s) {
247 - for (i=0; iswdigit(**s); (*s)++)
248 - i = 10*i + (**s-'0');
249 + for (i=0; iswdigit(**s); (*s)++) {
250 + if (i > INT_MAX/10U || **s-'0' > INT_MAX-10*i) i = -1;
251 + else i = 10*i + (**s-'0');
256 @@ -168,8 +170,8 @@ static const char sizeprefix['y'-'a'] = {
257 static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_arg, int *nl_type)
259 wchar_t *a, *z, *s=(wchar_t *)fmt;
260 - unsigned l10n=0, litpct, fl;
262 + unsigned l10n=0, fl;
267 @@ -181,20 +183,19 @@ static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_
271 + /* This error is only specified for snprintf, but since it's
272 + * unspecified for other forms, do the same. Stop immediately
273 + * on overflow; otherwise %n could produce wrong results. */
274 + if (l > INT_MAX - cnt) goto overflow;
276 /* Update output count, end loop when fmt is exhausted */
278 - if (l > INT_MAX - cnt) {
279 - if (!ferror(f)) errno = EOVERFLOW;
286 /* Handle literal text and %% format specifiers */
287 for (a=s; *s && *s!='%'; s++);
288 - litpct = wcsspn(s, L"%")/2; /* Optimize %%%% runs */
291 + for (z=s; s[0]=='%' && s[1]=='%'; z++, s+=2);
292 + if (z-a > INT_MAX-cnt) goto overflow;
296 @@ -222,9 +223,9 @@ static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_
298 w = f ? va_arg(*ap, int) : 0;
302 if (w<0) fl|=LEFT_ADJ, w=-w;
303 - } else if ((w=getint(&s))<0) return -1;
304 + } else if ((w=getint(&s))<0) goto overflow;
307 if (*s=='.' && s[1]=='*') {
308 @@ -235,24 +236,29 @@ static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_
310 p = f ? va_arg(*ap, int) : 0;
315 } else if (*s=='.') {
325 /* Format specifier state machine */
328 - if (OOB(*s)) return -1;
329 + if (OOB(*s)) goto inval;
331 st=states[st]S(*s++);
333 - if (!st) return -1;
334 + if (!st) goto inval;
336 /* Check validity of argument type (nl/normal) */
338 - if (argpos>=0) return -1;
339 + if (argpos>=0) goto inval;
341 if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos];
342 else if (f) pop_arg(&arg, st, ap);
343 @@ -285,8 +291,9 @@ static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_
347 - z = wmemchr(a, 0, p);
349 + z = a + wcsnlen(a, p<0 ? INT_MAX : p);
350 + if (p<0 && *z) goto overflow;
353 if (!(fl&LEFT_ADJ)) fprintf(f, "%*s", w-p, "");
355 @@ -298,9 +305,9 @@ static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_
357 if (!arg.p) arg.p = "(null)";
359 - if (p<0) p = INT_MAX;
360 - for (i=l=0; l<p && (i=mbtowc(&wc, bs, MB_LEN_MAX))>0; bs+=i, l++);
361 + for (i=l=0; l<(p<0?INT_MAX:p) && (i=mbtowc(&wc, bs, MB_LEN_MAX))>0; bs+=i, l++);
363 + if (p<0 && *bs) goto overflow;
366 if (!(fl&LEFT_ADJ)) fprintf(f, "%*s", w-p, "");
367 @@ -315,6 +322,7 @@ static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_
371 + if (xp && p<0) goto overflow;
372 snprintf(charfmt, sizeof charfmt, "%%%s%s%s%s%s*.*%c%c",
373 "#"+!(fl & ALT_FORM),
374 "+"+!(fl & MARK_POS),
375 @@ -341,6 +349,13 @@ static int wprintf_core(FILE *f, const wchar_t *fmt, va_list *ap, union arg *nl_
376 for (; i<=NL_ARGMAX && !nl_type[i]; i++);
377 if (i<=NL_ARGMAX) return -1;
388 int vfwprintf(FILE *restrict f, const wchar_t *restrict fmt, va_list ap)