X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=lib%2Fvasnprintf.c;h=ff80857305864a2ac93d98d7159af9048a67c7cf;hb=f792a03ccf327e0e80b60e6b7a013e7f89f9f5ca;hp=15ba9a13086a74e497113ecb41dafbc6f58f7aef;hpb=dda38ff89ea6c5c28da197cebfbe2d5b510eafb5;p=gnulib.git diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 15ba9a130..ff8085730 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -1,5 +1,5 @@ /* vsprintf with automatic memory allocation. - Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc. + Copyright (C) 1999, 2002-2004 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -40,7 +40,7 @@ #include /* abort(), malloc(), realloc(), free() */ #include /* memcpy(), strlen() */ #include /* errno */ -#include /* CHAR_BIT */ +#include /* CHAR_BIT, INT_MAX */ #include /* DBL_MAX_EXP, LDBL_MAX_EXP */ #if WIDE_CHAR_VERSION # include "wprintf-parse.h" @@ -48,14 +48,8 @@ # include "printf-parse.h" #endif -/* For those losing systems which don't have 'alloca' we have to add - some additional code emulating it. */ -#ifdef HAVE_ALLOCA -# define freea(p) /* nothing */ -#else -# define alloca(n) malloc (n) -# define freea(p) free (p) -#endif +/* Checked size_t computations. */ +#include "xsize.h" #ifdef HAVE_WCHAR_T # ifdef HAVE_WCSLEN @@ -136,17 +130,39 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar } { - CHAR_T *buf = - (CHAR_T *) alloca ((7 + d.max_width_length + d.max_precision_length + 6) - * sizeof (CHAR_T)); + size_t buf_neededlength; + CHAR_T *buf; + CHAR_T *buf_malloced; const CHAR_T *cp; - unsigned int i; + size_t i; DIRECTIVE *dp; /* Output string accumulator. */ CHAR_T *result; size_t allocated; size_t length; + /* Allocate a small buffer that will hold a directive passed to + sprintf or snprintf. */ + buf_neededlength = + xsum4 (7, d.max_width_length, d.max_precision_length, 6); +#if HAVE_ALLOCA + if (buf_neededlength < 4000 / sizeof (CHAR_T)) + { + buf = (CHAR_T *) alloca (buf_neededlength * sizeof (CHAR_T)); + buf_malloced = NULL; + } + else +#endif + { + size_t buf_memsize = xtimes (buf_neededlength, sizeof (CHAR_T)); + if (size_overflow_p (buf_memsize)) + goto out_of_memory_1; + buf = (CHAR_T *) malloc (buf_memsize); + if (buf == NULL) + goto out_of_memory_1; + buf_malloced = buf; + } + if (resultbuf != NULL) { result = resultbuf; @@ -162,28 +178,26 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar result is either == resultbuf or == NULL or malloc-allocated. If length > 0, then result != NULL. */ + /* Ensures that allocated >= needed. Aborts through a jump to + out_of_memory if needed is SIZE_MAX or otherwise too big. */ #define ENSURE_ALLOCATION(needed) \ if ((needed) > allocated) \ { \ + size_t memory_size; \ CHAR_T *memory; \ \ - allocated = (allocated > 0 ? 2 * allocated : 12); \ + allocated = (allocated > 0 ? xtimes (allocated, 2) : 12); \ if ((needed) > allocated) \ allocated = (needed); \ + memory_size = xtimes (allocated, sizeof (CHAR_T)); \ + if (size_overflow_p (memory_size)) \ + goto out_of_memory; \ if (result == resultbuf || result == NULL) \ - memory = (CHAR_T *) malloc (allocated * sizeof (CHAR_T)); \ + memory = (CHAR_T *) malloc (memory_size); \ else \ - memory = (CHAR_T *) realloc (result, allocated * sizeof (CHAR_T)); \ - \ + memory = (CHAR_T *) realloc (result, memory_size); \ if (memory == NULL) \ - { \ - if (!(result == resultbuf || result == NULL)) \ - free (result); \ - freea (buf); \ - CLEANUP (); \ - errno = ENOMEM; \ - return NULL; \ - } \ + goto out_of_memory; \ if (result == resultbuf && length > 0) \ memcpy (memory, result, length * sizeof (CHAR_T)); \ result = memory; \ @@ -194,10 +208,11 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar if (cp != dp->dir_start) { size_t n = dp->dir_start - cp; + size_t augmented_length = xsum (length, n); - ENSURE_ALLOCATION (length + n); + ENSURE_ALLOCATION (augmented_length); memcpy (result + length, cp, n * sizeof (CHAR_T)); - length += n; + length = augmented_length; } if (i == d.count) break; @@ -205,15 +220,18 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar /* Execute a single directive. */ if (dp->conversion == '%') { - if (!(dp->arg_index < 0)) + size_t augmented_length; + + if (!(dp->arg_index == ARG_NONE)) abort (); - ENSURE_ALLOCATION (length + 1); + augmented_length = xsum (length, 1); + ENSURE_ALLOCATION (augmented_length); result[length] = '%'; - length += 1; + length = augmented_length; } else { - if (!(dp->arg_index >= 0)) + if (!(dp->arg_index != ARG_NONE)) abort (); if (dp->conversion == 'n') @@ -248,34 +266,34 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar unsigned int prefix_count; int prefixes[2]; #if !USE_SNPRINTF - unsigned int tmp_length; + size_t tmp_length; CHAR_T tmpbuf[700]; CHAR_T *tmp; /* Allocate a temporary buffer of sufficient size for calling sprintf. */ { - unsigned int width; - unsigned int precision; + size_t width; + size_t precision; width = 0; if (dp->width_start != dp->width_end) { - if (dp->width_arg_index >= 0) + if (dp->width_arg_index != ARG_NONE) { int arg; if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); arg = a.arg[dp->width_arg_index].a.a_int; - width = (arg < 0 ? -arg : arg); + width = (arg < 0 ? (unsigned int) (-arg) : arg); } else { const CHAR_T *digitp = dp->width_start; do - width = width * 10 + (*digitp++ - '0'); + width = xsum (xtimes (width, 10), *digitp++ - '0'); while (digitp != dp->width_end); } } @@ -283,7 +301,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar precision = 6; if (dp->precision_start != dp->precision_end) { - if (dp->precision_arg_index >= 0) + if (dp->precision_arg_index != ARG_NONE) { int arg; @@ -297,9 +315,8 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar const CHAR_T *digitp = dp->precision_start + 1; precision = 0; - do - precision = precision * 10 + (*digitp++ - '0'); - while (digitp != dp->precision_end); + while (digitp != dp->precision_end) + precision = xsum (xtimes (precision, 10), *digitp++ - '0'); } } @@ -399,7 +416,6 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar * 2 /* estimate for FLAG_GROUP */ ) + 1 /* turn floor into ceil */ - + precision + 10; /* sign, decimal point etc. */ else # endif @@ -409,15 +425,15 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar * 2 /* estimate for FLAG_GROUP */ ) + 1 /* turn floor into ceil */ - + precision + 10; /* sign, decimal point etc. */ + tmp_length = xsum (tmp_length, precision); break; case 'e': case 'E': case 'g': case 'G': case 'a': case 'A': tmp_length = - precision - + 12; /* sign, decimal point, exponent etc. */ + 12; /* sign, decimal point, exponent etc. */ + tmp_length = xsum (tmp_length, precision); break; case 'c': @@ -432,14 +448,14 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar case 's': # ifdef HAVE_WCHAR_T if (type == TYPE_WIDE_STRING) -# if WIDE_CHAR_VERSION - tmp_length = - local_wcslen (a.arg[dp->arg_index].a.a_wide_string); -# else - tmp_length = - local_wcslen (a.arg[dp->arg_index].a.a_wide_string) - * MB_CUR_MAX; + { + tmp_length = + local_wcslen (a.arg[dp->arg_index].a.a_wide_string); + +# if !WIDE_CHAR_VERSION + tmp_length = xtimes (tmp_length, MB_CUR_MAX); # endif + } else # endif tmp_length = strlen (a.arg[dp->arg_index].a.a_string); @@ -461,24 +477,22 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar if (tmp_length < width) tmp_length = width; - tmp_length++; /* account for trailing NUL */ + tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ } if (tmp_length <= sizeof (tmpbuf) / sizeof (CHAR_T)) tmp = tmpbuf; else { - tmp = (CHAR_T *) malloc (tmp_length * sizeof (CHAR_T)); + size_t tmp_memsize = xtimes (tmp_length, sizeof (CHAR_T)); + + if (size_overflow_p (tmp_memsize)) + /* Overflow, would lead to out of memory. */ + goto out_of_memory; + tmp = (CHAR_T *) malloc (tmp_memsize); if (tmp == NULL) - { - /* Out of memory. */ - if (!(result == resultbuf || result == NULL)) - free (result); - freea (buf); - CLEANUP (); - errno = ENOMEM; - return NULL; - } + /* Out of memory. */ + goto out_of_memory; } #endif @@ -548,13 +562,13 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar /* Construct the arguments for calling snprintf or sprintf. */ prefix_count = 0; - if (dp->width_arg_index >= 0) + if (dp->width_arg_index != ARG_NONE) { if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int; } - if (dp->precision_arg_index >= 0) + if (dp->precision_arg_index != ARG_NONE) { if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); @@ -564,7 +578,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar #if USE_SNPRINTF /* Prepare checking whether snprintf returns the count via %n. */ - ENSURE_ALLOCATION (length + 1); + ENSURE_ALLOCATION (xsum (length, 1)); result[length] = '\0'; #endif @@ -769,7 +783,8 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar *and* it returns -1 (rather than the length that would have been required) when the buffer is too small. */ - size_t bigger_need = 2 * allocated + 12; + size_t bigger_need = + xsum (xtimes (allocated, 2), 12); ENSURE_ALLOCATION (bigger_need); continue; } @@ -784,7 +799,8 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar { if (!(result == resultbuf || result == NULL)) free (result); - freea (buf); + if (buf_malloced != NULL) + free (buf_malloced); CLEANUP (); errno = EINVAL; return NULL; @@ -803,10 +819,8 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar /* Need at least count bytes. But allocate proportionally, to avoid looping eternally if snprintf() reports a too small count. */ - size_t n = length + count; - - if (n < 2 * allocated) - n = 2 * allocated; + size_t n = + xmax (xsum (length, count), xtimes (allocated, 2)); ENSURE_ALLOCATION (n); #if USE_SNPRINTF @@ -831,7 +845,7 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar } /* Add the final NUL. */ - ENSURE_ALLOCATION (length + 1); + ENSURE_ALLOCATION (xsum (length, 1)); result[length] = '\0'; if (result != resultbuf && length + 1 < allocated) @@ -844,10 +858,32 @@ VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list ar result = memory; } - freea (buf); + if (buf_malloced != NULL) + free (buf_malloced); CLEANUP (); *lengthp = length; + if (length > INT_MAX) + goto length_overflow; return result; + + length_overflow: + /* We could produce such a big string, but its length doesn't fit into + an 'int'. POSIX says that snprintf() fails with errno = EOVERFLOW in + this case. */ + if (result != resultbuf) + free (result); + errno = EOVERFLOW; + return NULL; + + out_of_memory: + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + out_of_memory_1: + CLEANUP (); + errno = ENOMEM; + return NULL; } }