X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=src%2Fstdio%2Fvfprintf.c;h=45557951b5c3a63a5f01ebda220fab18d26667e2;hb=7d358599d4c8f793cfb42ee49ff5e1d107de6ee4;hp=67660121a71e3801319d7d0c4cea82f43c3ab1ee;hpb=cc44d9f2017855ad29c5c1301e8368158a4c2fa7;p=musl diff --git a/src/stdio/vfprintf.c b/src/stdio/vfprintf.c index 67660121..45557951 100644 --- a/src/stdio/vfprintf.c +++ b/src/stdio/vfprintf.c @@ -1,11 +1,20 @@ #include "stdio_impl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Some useful macros */ #define MAX(a,b) ((a)>(b) ? (a) : (b)) #define MIN(a,b) ((a)<(b) ? (a) : (b)) -#define CONCAT2(x,y) x ## y -#define CONCAT(x,y) CONCAT2(x,y) /* Convenient bit representation for modifier flags, which all fall * within 31 codepoints of the space character. */ @@ -19,14 +28,6 @@ #define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED) -#if UINT_MAX == ULONG_MAX -#define LONG_IS_INT -#endif - -#if SIZE_MAX != ULONG_MAX || UINTMAX_MAX != ULLONG_MAX -#define ODD_TYPES -#endif - /* State machine to accept length modifiers + conversion specifiers. * Result is 0 on failure, or an argument type to pop on success. */ @@ -35,23 +36,9 @@ enum { ZTPRE, JPRE, STOP, PTR, INT, UINT, ULLONG, -#ifndef LONG_IS_INT LONG, ULONG, -#else -#define LONG INT -#define ULONG UINT -#endif SHORT, USHORT, CHAR, UCHAR, -#ifdef ODD_TYPES LLONG, SIZET, IMAX, UMAX, PDIFF, UIPTR, -#else -#define LLONG ULLONG -#define SIZET ULONG -#define IMAX LLONG -#define UMAX ULLONG -#define PDIFF LONG -#define UIPTR ULONG -#endif DBL, LDBL, NOARG, MAXSTATE @@ -73,6 +60,8 @@ static const unsigned char states[]['z'-'A'+1] = { }, { /* 1: l-prefixed */ S('d') = LONG, S('i') = LONG, S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG, + S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL, + S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL, S('c') = INT, S('s') = PTR, S('n') = PTR, S('l') = LLPRE, }, { /* 2: ll-prefixed */ @@ -119,29 +108,23 @@ union arg static void pop_arg(union arg *arg, int type, va_list *ap) { - /* Give the compiler a hint for optimizing the switch. */ - if ((unsigned)type > MAXSTATE) return; switch (type) { case PTR: arg->p = va_arg(*ap, void *); break; case INT: arg->i = va_arg(*ap, int); break; case UINT: arg->i = va_arg(*ap, unsigned int); -#ifndef LONG_IS_INT break; case LONG: arg->i = va_arg(*ap, long); break; case ULONG: arg->i = va_arg(*ap, unsigned long); -#endif break; case ULLONG: arg->i = va_arg(*ap, unsigned long long); break; case SHORT: arg->i = (short)va_arg(*ap, int); break; case USHORT: arg->i = (unsigned short)va_arg(*ap, int); break; case CHAR: arg->i = (signed char)va_arg(*ap, int); break; case UCHAR: arg->i = (unsigned char)va_arg(*ap, int); -#ifdef ODD_TYPES break; case LLONG: arg->i = va_arg(*ap, long long); break; case SIZET: arg->i = va_arg(*ap, size_t); break; case IMAX: arg->i = va_arg(*ap, intmax_t); break; case UMAX: arg->i = va_arg(*ap, uintmax_t); break; case PDIFF: arg->i = va_arg(*ap, ptrdiff_t); break; case UIPTR: arg->i = (uintptr_t)va_arg(*ap, void *); -#endif break; case DBL: arg->f = va_arg(*ap, double); break; case LDBL: arg->f = va_arg(*ap, long double); } @@ -149,7 +132,7 @@ static void pop_arg(union arg *arg, int type, va_list *ap) static void out(FILE *f, const char *s, size_t l) { - __fwritex((void *)s, l, f); + if (!(f->flags & F_ERR)) __fwritex((void *)s, l, f); } static void pad(FILE *f, char c, int w, int l, int fl) @@ -187,9 +170,17 @@ static char *fmt_u(uintmax_t x, char *s) return s; } +/* Do not override this check. The floating point printing code below + * depends on the float.h constants being right. If they are wrong, it + * may overflow the stack. */ +#if LDBL_MANT_DIG == 53 +typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)]; +#endif + static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) { - uint32_t big[(LDBL_MAX_EXP+LDBL_MANT_DIG)/9+1]; + uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion + + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion uint32_t *a, *d, *r, *z; int e2=0, e, i, j, l; char buf[9+LDBL_MANT_DIG/4], *s; @@ -198,7 +189,7 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr; pl=1; - if (y<0 || 1/y<0) { + if (signbit(y)) { y=-y; } else if (fl & MARK_POS) { prefix+=3; @@ -208,7 +199,7 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) if (!isfinite(y)) { char *s = (t&32)?"inf":"INF"; - if (y!=y) s=(t&32)?"nan":"NAN", pl=0; + if (y!=y) s=(t&32)?"nan":"NAN"; pad(f, ' ', w, 3+pl, fl&~ZERO_PAD); out(f, prefix, pl); out(f, s, 3); @@ -230,6 +221,7 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) else re=LDBL_MANT_DIG/4-1-p; if (re) { + round *= 1<<(LDBL_MANT_DIG%4); while (re--) round*=16; if (*prefix=='-') { y=-y; @@ -255,6 +247,8 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.'; } while (y); + if (p > INT_MAX-2-(ebuf-estr)-pl) + return -1; if (p && s-buf-2 < p) l = (p+2) + (ebuf-estr); else @@ -289,13 +283,13 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) *d = x % 1000000000; carry = x / 1000000000; } - if (!z[-1] && z>a) z--; if (carry) *--a = carry; + while (z>a && !z[-1]) z--; e2-=sh; } while (e2<0) { - uint32_t carry=0, *z2; - int sh=MIN(9,-e2); + uint32_t carry=0, *b; + int sh=MIN(9,-e2), need=1+(p+LDBL_MANT_DIG/3U+8)/9; for (d=a; d>sh) + carry; @@ -304,8 +298,8 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) if (!*a) a++; if (carry) *z++ = carry; /* Avoid (slow!) computation past requested precision */ - z2 = ((t|32)=='f' ? r : a) + 2 + p/9; - z = MIN(z, z2); + b = (t|32)=='f' ? r : a; + if (z-b > need) z = b+need; e2+=sh; } @@ -317,16 +311,17 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) if (j < 9*(z-r-1)) { uint32_t x; /* We avoid C's broken division of negative numbers */ - d = r + 1 + (j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP; + d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP); j += 9*LDBL_MAX_EXP; j %= 9; for (i=10, j++; j<9; i*=10, j++); x = *d % i; /* Are there any significant digits past j? */ if (x || d+1!=z) { - long double round = CONCAT(0x1p,LDBL_MANT_DIG); + long double round = 2/LDBL_EPSILON; long double small; - if (*d/i & 1) round += 2; + if ((*d/i & 1) || (i==1000000000 && d>a && (d[-1]&1))) + round += 2; if (x 999999999) { *d--=0; + if (d=i; i*=10, e++); } } if (z>d+1) z=d+1; - for (; !z[-1] && z>a; z--); } + for (; z>a && !z[-1]; z--); if ((t|32)=='g') { if (!p) p++; @@ -366,17 +361,22 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) p = MIN(p,MAX(0,9*(z-r-1)+e-j)); } } + if (p > INT_MAX-1-(p || (fl&ALT_FORM))) + return -1; l = 1 + p + (p || (fl&ALT_FORM)); if ((t|32)=='f') { + if (e > INT_MAX-l) return -1; if (e>0) l+=e; } else { estr=fmt_u(e<0 ? -e : e, ebuf); while(ebuf-estr<2) *--estr='0'; *--estr = (e<0 ? '-' : '+'); *--estr = t; + if (ebuf-estr > INT_MAX-l) return -1; l += ebuf-estr; } + if (l > INT_MAX-pl) return -1; pad(f, ' ', w, pl+l, fl); out(f, prefix, pl); pad(f, '0', w, pl+l, fl^ZERO_PAD); @@ -420,21 +420,23 @@ static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) static int getint(char **s) { int i; - for (i=0; isdigit(**s); (*s)++) - i = 10*i + (**s-'0'); + for (i=0; isdigit(**s); (*s)++) { + if (i > INT_MAX/10U || **s-'0' > INT_MAX-10*i) i = -1; + else i = 10*i + (**s-'0'); + } return i; } static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, int *nl_type) { char *a, *z, *s=(char *)fmt; - unsigned l10n=0, litpct, fl; - int w, p; + unsigned l10n=0, fl; + int w, p, xp; union arg arg; int argpos; unsigned st, ps; int cnt=0, l=0; - int i; + size_t i; char buf[sizeof(uintmax_t)*3+3+LDBL_MANT_DIG/4]; const char *prefix; int t, pl; @@ -442,20 +444,19 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, char mb[4]; for (;;) { + /* This error is only specified for snprintf, but since it's + * unspecified for other forms, do the same. Stop immediately + * on overflow; otherwise %n could produce wrong results. */ + if (l > INT_MAX - cnt) goto overflow; + /* Update output count, end loop when fmt is exhausted */ - if (cnt >= 0) { - if (l > INT_MAX - cnt) { - errno = EOVERFLOW; - cnt = -1; - } else cnt += l; - } + cnt += l; if (!*s) break; /* Handle literal text and %% format specifiers */ for (a=s; *s && *s!='%'; s++); - litpct = strspn(s, "%")/2; /* Optimize %%%% runs */ - z = s+litpct; - s += 2*litpct; + for (z=s; s[0]=='%' && s[1]=='%'; z++, s+=2); + if (z-a > INT_MAX-cnt) goto overflow; l = z-a; if (f) out(f, a, l); if (l) continue; @@ -477,47 +478,53 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, if (*s=='*') { if (isdigit(s[1]) && s[2]=='$') { l10n=1; - nl_type[s[1]-'0'] = INT; - w = nl_arg[s[1]-'0'].i; + if (!f) nl_type[s[1]-'0'] = INT, w = 0; + else w = nl_arg[s[1]-'0'].i; s+=3; } else if (!l10n) { w = f ? va_arg(*ap, int) : 0; s++; - } else return -1; + } else goto inval; if (w<0) fl|=LEFT_ADJ, w=-w; - } else if ((w=getint(&s))<0) return -1; + } else if ((w=getint(&s))<0) goto overflow; /* Read precision */ if (*s=='.' && s[1]=='*') { if (isdigit(s[2]) && s[3]=='$') { - nl_type[s[2]-'0'] = INT; - p = nl_arg[s[2]-'0'].i; + if (!f) nl_type[s[2]-'0'] = INT, p = 0; + else p = nl_arg[s[2]-'0'].i; s+=4; } else if (!l10n) { p = f ? va_arg(*ap, int) : 0; s+=2; - } else return -1; + } else goto inval; + xp = (p>=0); } else if (*s=='.') { s++; p = getint(&s); - } else p = -1; + xp = 1; + } else { + p = -1; + xp = 0; + } /* Format specifier state machine */ st=0; do { - if (OOB(*s)) return -1; + if (OOB(*s)) goto inval; ps=st; st=states[st]S(*s++); } while (st-1=0) return -1; - else if (!f) continue; + if (argpos>=0) goto inval; } else { - if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos]; - else if (f) pop_arg(&arg, st, ap); + if (argpos>=0) { + if (!f) nl_type[argpos]=st; + else arg=nl_arg[argpos]; + } else if (f) pop_arg(&arg, st, ap); else return 0; } @@ -556,7 +563,7 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, if (0) { case 'o': a = fmt_o(arg.i, z); - if ((fl&ALT_FORM) && arg.i) prefix+=5, pl=1; + if ((fl&ALT_FORM) && p=0) fl &= ~ZERO_PAD; p = MAX(p, z-a + !arg.i); break; case 'c': @@ -584,10 +592,10 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, case 'm': if (1) a = strerror(errno); else case 's': - a = arg.p; - z = memchr(a, 0, p); - if (!z) z=a+p; - else p=z-a; + a = arg.p ? arg.p : "(null)"; + z = a + strnlen(a, p<0 ? INT_MAX : p); + if (p<0 && *z) goto overflow; + p = z-a; fl &= ~ZERO_PAD; break; case 'C': @@ -597,24 +605,29 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, p = -1; case 'S': ws = arg.p; - for (i=0; *ws && (l=wctomb(mb, *ws++))>=0 && l<=0U+p-i; i+=l); + for (i=l=0; i

=0 && l<=p-i; i+=l); if (l<0) return -1; + if (i > INT_MAX) goto overflow; p = i; pad(f, ' ', w, p, fl); ws = arg.p; - for (i=0; *ws && i+(l=wctomb(mb, *ws++))<=p; i+=l) + for (i=0; i<0U+p && *ws && i+(l=wctomb(mb, *ws++))<=p; i+=l) out(f, mb, l); pad(f, ' ', w, p, fl^LEFT_ADJ); l = w>p ? w : p; continue; case 'e': case 'f': case 'g': case 'a': case 'E': case 'F': case 'G': case 'A': + if (xp && p<0) goto overflow; l = fmt_fp(f, arg.f, w, p, fl, t); + if (l<0) goto overflow; continue; } if (p < z-a) p = z-a; + if (p > INT_MAX-pl) goto overflow; if (w < pl+p) w = pl+p; + if (w > INT_MAX-cnt) goto overflow; pad(f, ' ', w, pl+p, fl); out(f, prefix, pl); @@ -632,28 +645,44 @@ static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, for (i=1; i<=NL_ARGMAX && nl_type[i]; i++) pop_arg(nl_arg+i, nl_type[i], ap); for (; i<=NL_ARGMAX && !nl_type[i]; i++); - if (i<=NL_ARGMAX) return -1; + if (i<=NL_ARGMAX) goto inval; return 1; + +inval: + errno = EINVAL; + return -1; +overflow: + errno = EOVERFLOW; + return -1; } -int vfprintf(FILE *f, const char *fmt, va_list ap) +int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) { va_list ap2; int nl_type[NL_ARGMAX+1] = {0}; union arg nl_arg[NL_ARGMAX+1]; unsigned char internal_buf[80], *saved_buf = 0; + int olderr; int ret; + /* the copy allows passing va_list* even if va_list is an array */ va_copy(ap2, ap); - if (printf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) return -1; + if (printf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) { + va_end(ap2); + return -1; + } FLOCK(f); + olderr = f->flags & F_ERR; + if (f->mode < 1) f->flags &= ~F_ERR; if (!f->buf_size) { saved_buf = f->buf; f->buf = internal_buf; f->buf_size = sizeof internal_buf; + f->wpos = f->wbase = f->wend = 0; } - ret = printf_core(f, fmt, &ap2, nl_arg, nl_type); + if (!f->wend && __towrite(f)) ret = -1; + else ret = printf_core(f, fmt, &ap2, nl_arg, nl_type); if (saved_buf) { f->write(f, 0, 0); if (!f->wpos) ret = -1; @@ -661,6 +690,8 @@ int vfprintf(FILE *f, const char *fmt, va_list ap) f->buf_size = 0; f->wpos = f->wbase = f->wend = 0; } + if (f->flags & F_ERR) ret = -1; + f->flags |= olderr; FUNLOCK(f); va_end(ap2); return ret;