bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Bug-gnulib] proposed vasnprintf patches for address arithmetic and stac


From: Paul Eggert
Subject: [Bug-gnulib] proposed vasnprintf patches for address arithmetic and stack overflow
Date: 15 Nov 2003 00:07:18 -0800
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.3

Here are some proposed patches for the vasnprintf module of gnulib.
I don't use this code myself, so I haven't tested it other than
by compiling it.

2003-11-14  Paul Eggert  <address@hidden>

        Fix some problems with address arithmetic overflow.  Some of
        them are more feasible than others (e.g., a print width exceeding
        INT_MAX is more feasible than having more than UINT_MAX args), but
        it's easy enough to fix them all while we're in the neighborhood.

        I also fixed a potential alloca overflow while I was in the
        neighborhood.

        * printf-args.h (arguments): Use size_t for sizes.
        * printf-args.c (printf_fetchargs): Likewise.
        * printf-parse.h (char_directive, char_directives): Likewise.
        * printf-parse.c (printf_parse, REGISTER_ARG): Likewise.
        * vasnprintf.c (vasnprintf): Likewise.
        * printf-parse.c: Include "xsize.h".
        * printf-parse.c (printf_parse, REGISTER_ARG):
        Check for arithmetic overflow in size calculations.
        * vasnprintf.c (vasnprintf): Likewise.
        * vasnprintf.c (ALLOCA_LIMIT): New macro.
        (freea): Remove.  All uses removed.
        (alloca) [!HAVE_ALLOCA]: Define to NULL, not to malloc,
        to let the reader know that it will never be invoked.
        (vasnprintf, CLEANUP): Record address of any storage that
        needs to be freed in a local variable needs_freeing.
        Initialize 'result' and 'allocated' early, so that CLEANUP
        can rely on their contents.  Use alloca only for buffers smaller
        than ALLOCA_LIMIT.  Establish a new out_of_memory label to
        remove duplicate code for freeing storage.
        (vasnprintf): Don't compute (arg < 0 ? -arg : arg), as this
        mishandles the case when arg == INT_MIN && INT_MIN < -INT_MAX
        && INT_MAX < SIZE_MAX / 2.

Index: lib/printf-args.h
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/printf-args.h,v
retrieving revision 1.2
diff -p -u -r1.2 printf-args.h
--- lib/printf-args.h   14 Jul 2003 22:44:04 -0000      1.2
+++ lib/printf-args.h   15 Nov 2003 07:42:55 -0000
@@ -116,7 +116,7 @@ argument;
 
 typedef struct
 {
-  unsigned int count;
+  size_t count;
   argument *arg;
 }
 arguments;
Index: lib/printf-args.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/printf-args.c,v
retrieving revision 1.2
diff -p -u -r1.2 printf-args.c
--- lib/printf-args.c   14 Jul 2003 22:44:04 -0000      1.2
+++ lib/printf-args.c   15 Nov 2003 07:42:55 -0000
@@ -28,7 +28,7 @@ STATIC
 int
 printf_fetchargs (va_list args, arguments *a)
 {
-  unsigned int i;
+  size_t i;
   argument *ap;
 
   for (i = 0, ap = &a->arg[0]; i < a->count; i++, ap++)
Index: lib/printf-parse.h
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/printf-parse.h,v
retrieving revision 1.2
diff -p -u -r1.2 printf-parse.h
--- lib/printf-parse.h  14 Jul 2003 22:44:04 -0000      1.2
+++ lib/printf-parse.h  15 Nov 2003 07:42:56 -0000
@@ -37,22 +37,22 @@ typedef struct
   int flags;
   const char* width_start;
   const char* width_end;
-  int width_arg_index;
+  size_t width_arg_index;
   const char* precision_start;
   const char* precision_end;
-  int precision_arg_index;
+  size_t precision_arg_index;
   char conversion; /* d i o u x X f e E g G c s p n U % but not C S */
-  int arg_index;
+  size_t arg_index;
 }
 char_directive;
 
 /* A parsed format string.  */
 typedef struct
 {
-  unsigned int count;
+  size_t count;
   char_directive *dir;
-  unsigned int max_width_length;
-  unsigned int max_precision_length;
+  size_t max_width_length;
+  size_t max_precision_length;
 }
 char_directives;
 
Index: lib/printf-parse.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/printf-parse.c,v
retrieving revision 1.2
diff -p -u -r1.2 printf-parse.c
--- lib/printf-parse.c  14 Jul 2003 22:44:04 -0000      1.2
+++ lib/printf-parse.c  15 Nov 2003 07:42:56 -0000
@@ -25,6 +25,8 @@
 /* Get size_t, NULL.  */
 #include <stddef.h>
 
+#include "xsize.h"
+
 /* Get intmax_t.  */
 #if HAVE_STDINT_H_WITH_UINTMAX
 # include <stdint.h>
@@ -43,11 +45,11 @@ int
 printf_parse (const char *format, char_directives *d, arguments *a)
 {
   const char *cp = format;             /* pointer into format */
-  int arg_posn = 0;            /* number of regular arguments consumed */
-  unsigned int d_allocated;            /* allocated elements of d->dir */
-  unsigned int a_allocated;            /* allocated elements of a->arg */
-  unsigned int max_width_length = 0;
-  unsigned int max_precision_length = 0;
+  size_t arg_posn = 0;         /* number of regular arguments consumed */
+  size_t d_allocated;                  /* allocated elements of d->dir */
+  size_t a_allocated;                  /* allocated elements of a->arg */
+  size_t max_width_length = 0;
+  size_t max_precision_length = 0;
 
   d->count = 0;
   d_allocated = 1;
@@ -62,16 +64,18 @@ printf_parse (const char *format, char_d
 
 #define REGISTER_ARG(_index_,_type_) \
   {                                                                    \
-    unsigned int n = (_index_);                                                
\
+    size_t n = (_index_);                                              \
     if (n >= a_allocated)                                              \
       {                                                                        
\
+       size_t size;                                                    \
        argument *memory;                                               \
        a_allocated = 2 * a_allocated;                                  \
        if (a_allocated <= n)                                           \
          a_allocated = n + 1;                                          \
-       memory = (a->arg                                                \
-                 ? realloc (a->arg, a_allocated * sizeof (argument))   \
-                 : malloc (a_allocated * sizeof (argument)));          \
+       size = xtimes (a_allocated, sizeof *memory);                    \
+       if (size_overflow_p (size))                                     \
+         goto error;                                                   \
+       memory = a->arg ? realloc (a->arg, size) : malloc (size);       \
        if (memory == NULL)                                             \
          /* Out of memory.  */                                         \
          goto error;                                                   \
@@ -91,7 +95,7 @@ printf_parse (const char *format, char_d
       char c = *cp++;
       if (c == '%')
        {
-         int arg_index = -1;
+         size_t arg_index = SIZE_MAX;
          char_directive *dp = &d->dir[d->count];/* pointer to next directive */
 
          /* Initialize the next directive.  */
@@ -99,11 +103,11 @@ printf_parse (const char *format, char_d
          dp->flags = 0;
          dp->width_start = NULL;
          dp->width_end = NULL;
-         dp->width_arg_index = -1;
+         dp->width_arg_index = SIZE_MAX;
          dp->precision_start = NULL;
          dp->precision_end = NULL;
-         dp->precision_arg_index = -1;
-         dp->arg_index = -1;
+         dp->precision_arg_index = SIZE_MAX;
+         dp->arg_index = SIZE_MAX;
 
          /* Test for positional argument.  */
          if (*cp >= '0' && *cp <= '9')
@@ -114,7 +118,7 @@ printf_parse (const char *format, char_d
                ;
              if (*np == '$')
                {
-                 unsigned int n = 0;
+                 size_t n = 0;
 
                  for (np = cp; *np >= '0' && *np <= '9'; np++)
                    n = 10 * n + (*np - '0');
@@ -181,7 +185,7 @@ printf_parse (const char *format, char_d
                    ;
                  if (*np == '$')
                    {
-                     unsigned int n = 0;
+                     size_t n = 0;
 
                      for (np = cp; *np >= '0' && *np <= '9'; np++)
                        n = 10 * n + (*np - '0');
@@ -192,13 +196,13 @@ printf_parse (const char *format, char_d
                      cp = np + 1;
                    }
                }
-             if (dp->width_arg_index < 0)
+             if (dp->width_arg_index == SIZE_MAX)
                dp->width_arg_index = arg_posn++;
              REGISTER_ARG (dp->width_arg_index, TYPE_INT);
            }
          else if (*cp >= '0' && *cp <= '9')
            {
-             unsigned int width_length;
+             size_t width_length;
 
              dp->width_start = cp;
              for (; *cp >= '0' && *cp <= '9'; cp++)
@@ -230,7 +234,7 @@ printf_parse (const char *format, char_d
                        ;
                      if (*np == '$')
                        {
-                         unsigned int n = 0;
+                         size_t n = 0;
 
                          for (np = cp; *np >= '0' && *np <= '9'; np++)
                            n = 10 * n + (*np - '0');
@@ -241,13 +245,13 @@ printf_parse (const char *format, char_d
                          cp = np + 1;
                        }
                    }
-                 if (dp->precision_arg_index < 0)
+                 if (dp->precision_arg_index == SIZE_MAX)
                    dp->precision_arg_index = arg_posn++;
                  REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
                }
              else
                {
-                 unsigned int precision_length;
+                 size_t precision_length;
 
                  dp->precision_start = cp - 1;
                  for (; *cp >= '0' && *cp <= '9'; cp++)
@@ -439,7 +443,7 @@ printf_parse (const char *format, char_d
            if (type != TYPE_NONE)
              {
                dp->arg_index = arg_index;
-               if (dp->arg_index < 0)
+               if (dp->arg_index == SIZE_MAX)
                  dp->arg_index = arg_posn++;
                REGISTER_ARG (dp->arg_index, type);
              }
@@ -450,10 +454,14 @@ printf_parse (const char *format, char_d
          d->count++;
          if (d->count >= d_allocated)
            {
+             size_t size;
              char_directive *memory;
 
              d_allocated = 2 * d_allocated;
-             memory = realloc (d->dir, d_allocated * sizeof (char_directive));
+             size = xtimes (d_allocated, sizeof *memory);
+             if (size_overflow_p (size))
+               goto error;
+             memory = realloc (d->dir, size);
              if (memory == NULL)
                /* Out of memory.  */
                goto error;
Index: lib/vasnprintf.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/vasnprintf.c,v
retrieving revision 1.8
diff -p -u -r1.8 vasnprintf.c
--- lib/vasnprintf.c    20 Oct 2003 10:51:59 -0000      1.8
+++ lib/vasnprintf.c    15 Nov 2003 07:42:56 -0000
@@ -37,14 +37,14 @@
 #include <limits.h>    /* CHAR_BIT */
 #include <float.h>     /* DBL_MAX_EXP, LDBL_MAX_EXP */
 #include "printf-parse.h"
+#include "xsize.h"
 
-/* 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 */
+# define ALLOCA_LIMIT 8000
 #else
-# define alloca(n) malloc (n)
-# define freea(p) free (p)
+# define ALLOCA_LIMIT 0
+# undef alloca
+# define alloca(n) NULL
 #endif
 
 #ifdef HAVE_WCHAR_T
@@ -70,6 +70,7 @@ vasnprintf (char *resultbuf, size_t *len
 {
   char_directives d;
   arguments a;
+  void *needs_freeing = NULL;
 
   if (printf_parse (format, &d, &a) < 0)
     {
@@ -78,6 +79,8 @@ vasnprintf (char *resultbuf, size_t *len
     }
 
 #define CLEANUP() \
+  if (needs_freeing)                                                   \
+    free (needs_freeing);                                              \
   free (d.dir);                                                                
\
   if (a.arg)                                                           \
     free (a.arg);
@@ -90,26 +93,27 @@ vasnprintf (char *resultbuf, size_t *len
     }
 
   {
-    char *buf =
-      (char *) alloca (7 + d.max_width_length + d.max_precision_length + 6);
+    size_t bufsize = xsum3 (7 + 6, d.max_width_length, d.max_precision_length);
+    char *buf;
     const char *cp;
-    unsigned int i;
+    size_t i;
     char_directive *dp;
     /* Output string accumulator.  */
-    char *result;
-    size_t allocated;
+    char *result = NULL;
+    size_t allocated = 0;
     size_t length;
 
+    if (bufsize < ALLOCA_LIMIT)
+      buf = (char *) alloca (bufsize);
+    else if (size_overflow_p (bufsize)
+            || ! (buf = needs_freeing = malloc (bufsize)))
+      goto out_of_memory;
+
     if (resultbuf != NULL)
       {
        result = resultbuf;
        allocated = *lengthp;
       }
-    else
-      {
-       result = NULL;
-       allocated = 0;
-      }
     length = 0;
     /* Invariants:
        result is either == resultbuf or == NULL or malloc-allocated.
@@ -129,14 +133,7 @@ vasnprintf (char *resultbuf, size_t *len
          memory = (char *) realloc (result, allocated);                \
                                                                        \
        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);                              \
        result = memory;                                                \
@@ -147,8 +144,11 @@ vasnprintf (char *resultbuf, size_t *len
        if (cp != dp->dir_start)
          {
            size_t n = dp->dir_start - cp;
+           size_t needed = xsum (length, n);
 
-           ENSURE_ALLOCATION (length + n);
+           if (size_overflow_p (needed))
+             goto out_of_memory;
+           ENSURE_ALLOCATION (needed);
            memcpy (result + length, cp, n);
            length += n;
          }
@@ -158,7 +158,7 @@ vasnprintf (char *resultbuf, size_t *len
        /* Execute a single directive.  */
        if (dp->conversion == '%')
          {
-           if (!(dp->arg_index < 0))
+           if (!(dp->arg_index == SIZE_MAX))
              abort ();
            ENSURE_ALLOCATION (length + 1);
            result[length] = '%';
@@ -166,7 +166,7 @@ vasnprintf (char *resultbuf, size_t *len
          }
        else
          {
-           if (!(dp->arg_index >= 0))
+           if (!(dp->arg_index != SIZE_MAX))
              abort ();
 
            if (dp->conversion == 'n')
@@ -201,27 +201,29 @@ vasnprintf (char *resultbuf, size_t *len
                unsigned int prefix_count;
                int prefixes[2];
 #if !HAVE_SNPRINTF
-               unsigned int tmp_length;
+               size_t tmp_length;
                char tmpbuf[700];
                char *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 != SIZE_MAX)
                        {
                          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;
+                         if (arg < 0)
+                           width = -width;
                        }
                      else
                        {
@@ -236,7 +238,7 @@ vasnprintf (char *resultbuf, size_t *len
                  precision = 6;
                  if (dp->precision_start != dp->precision_end)
                    {
-                     if (dp->precision_arg_index >= 0)
+                     if (dp->precision_arg_index != SIZE_MAX)
                        {
                          int arg;
 
@@ -263,28 +265,28 @@ vasnprintf (char *resultbuf, size_t *len
 # ifdef HAVE_LONG_LONG
                      if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
                        tmp_length =
-                         (unsigned int) (sizeof (unsigned long long) * CHAR_BIT
-                                         * 0.30103 /* binary -> decimal */
-                                         * 2 /* estimate for FLAG_GROUP */
-                                        )
+                         (size_t) (sizeof (unsigned long long) * CHAR_BIT
+                                   * 0.30103 /* binary -> decimal */
+                                   * 2 /* estimate for FLAG_GROUP */
+                                   )
                          + 1 /* turn floor into ceil */
                          + 1; /* account for leading sign */
                      else
 # endif
                      if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
                        tmp_length =
-                         (unsigned int) (sizeof (unsigned long) * CHAR_BIT
-                                         * 0.30103 /* binary -> decimal */
-                                         * 2 /* estimate for FLAG_GROUP */
-                                        )
+                         (size_t) (sizeof (unsigned long) * CHAR_BIT
+                                   * 0.30103 /* binary -> decimal */
+                                   * 2 /* estimate for FLAG_GROUP */
+                                   )
                          + 1 /* turn floor into ceil */
                          + 1; /* account for leading sign */
                      else
                        tmp_length =
-                         (unsigned int) (sizeof (unsigned int) * CHAR_BIT
-                                         * 0.30103 /* binary -> decimal */
-                                         * 2 /* estimate for FLAG_GROUP */
-                                        )
+                         (size_t) (sizeof (unsigned int) * CHAR_BIT
+                                   * 0.30103 /* binary -> decimal */
+                                   * 2 /* estimate for FLAG_GROUP */
+                                   )
                          + 1 /* turn floor into ceil */
                          + 1; /* account for leading sign */
                      break;
@@ -293,25 +295,25 @@ vasnprintf (char *resultbuf, size_t *len
 # ifdef HAVE_LONG_LONG
                      if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
                        tmp_length =
-                         (unsigned int) (sizeof (unsigned long long) * CHAR_BIT
-                                         * 0.333334 /* binary -> octal */
-                                        )
+                         (size_t) (sizeof (unsigned long long) * CHAR_BIT
+                                   * 0.333334 /* binary -> octal */
+                                   )
                          + 1 /* turn floor into ceil */
                          + 1; /* account for leading sign */
                      else
 # endif
                      if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
                        tmp_length =
-                         (unsigned int) (sizeof (unsigned long) * CHAR_BIT
-                                         * 0.333334 /* binary -> octal */
-                                        )
+                         (size_t) (sizeof (unsigned long) * CHAR_BIT
+                                   * 0.333334 /* binary -> octal */
+                                   )
                          + 1 /* turn floor into ceil */
                          + 1; /* account for leading sign */
                      else
                        tmp_length =
-                         (unsigned int) (sizeof (unsigned int) * CHAR_BIT
-                                         * 0.333334 /* binary -> octal */
-                                        )
+                         (size_t) (sizeof (unsigned int) * CHAR_BIT
+                                   * 0.333334 /* binary -> octal */
+                                   )
                          + 1 /* turn floor into ceil */
                          + 1; /* account for leading sign */
                      break;
@@ -320,25 +322,25 @@ vasnprintf (char *resultbuf, size_t *len
 # ifdef HAVE_LONG_LONG
                      if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
                        tmp_length =
-                         (unsigned int) (sizeof (unsigned long long) * CHAR_BIT
-                                         * 0.25 /* binary -> hexadecimal */
-                                        )
+                         (size_t) (sizeof (unsigned long long) * CHAR_BIT
+                                   * 0.25 /* binary -> hexadecimal */
+                                   )
                          + 1 /* turn floor into ceil */
                          + 2; /* account for leading sign or alternate form */
                      else
 # endif
                      if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
                        tmp_length =
-                         (unsigned int) (sizeof (unsigned long) * CHAR_BIT
-                                         * 0.25 /* binary -> hexadecimal */
-                                        )
+                         (size_t) (sizeof (unsigned long) * CHAR_BIT
+                                   * 0.25 /* binary -> hexadecimal */
+                                   )
                          + 1 /* turn floor into ceil */
                          + 2; /* account for leading sign or alternate form */
                      else
                        tmp_length =
-                         (unsigned int) (sizeof (unsigned int) * CHAR_BIT
-                                         * 0.25 /* binary -> hexadecimal */
-                                        )
+                         (size_t) (sizeof (unsigned int) * CHAR_BIT
+                                   * 0.25 /* binary -> hexadecimal */
+                                   )
                          + 1 /* turn floor into ceil */
                          + 2; /* account for leading sign or alternate form */
                      break;
@@ -347,20 +349,20 @@ vasnprintf (char *resultbuf, size_t *len
 # ifdef HAVE_LONG_DOUBLE
                      if (type == TYPE_LONGDOUBLE)
                        tmp_length =
-                         (unsigned int) (LDBL_MAX_EXP
-                                         * 0.30103 /* binary -> decimal */
-                                         * 2 /* estimate for FLAG_GROUP */
-                                        )
+                         (size_t) (LDBL_MAX_EXP
+                                   * 0.30103 /* binary -> decimal */
+                                   * 2 /* estimate for FLAG_GROUP */
+                                   )
                          + 1 /* turn floor into ceil */
                          + precision
                          + 10; /* sign, decimal point etc. */
                      else
 # endif
                        tmp_length =
-                         (unsigned int) (DBL_MAX_EXP
-                                         * 0.30103 /* binary -> decimal */
-                                         * 2 /* estimate for FLAG_GROUP */
-                                        )
+                         (size_t) (DBL_MAX_EXP
+                                   * 0.30103 /* binary -> decimal */
+                                   * 2 /* estimate for FLAG_GROUP */
+                                   )
                          + 1 /* turn floor into ceil */
                          + precision
                          + 10; /* sign, decimal point etc. */
@@ -395,9 +397,9 @@ vasnprintf (char *resultbuf, size_t *len
 
                    case 'p':
                      tmp_length =
-                       (unsigned int) (sizeof (void *) * CHAR_BIT
-                                       * 0.25 /* binary -> hexadecimal */
-                                      )
+                       (size_t) (sizeof (void *) * CHAR_BIT
+                                 * 0.25 /* binary -> hexadecimal */
+                                 )
                          + 1 /* turn floor into ceil */
                          + 2; /* account for leading 0x */
                      break;
@@ -418,15 +420,7 @@ vasnprintf (char *resultbuf, size_t *len
                  {
                    tmp = (char *) malloc (tmp_length);
                    if (tmp == NULL)
-                     {
-                       /* Out of memory.  */
-                       if (!(result == resultbuf || result == NULL))
-                         free (result);
-                       freea (buf);
-                       CLEANUP ();
-                       errno = ENOMEM;
-                       return NULL;
-                     }
+                     goto out_of_memory;
                  }
 #endif
 
@@ -496,13 +490,13 @@ vasnprintf (char *resultbuf, size_t *len
 
                /* Construct the arguments for calling snprintf or sprintf.  */
                prefix_count = 0;
-               if (dp->width_arg_index >= 0)
+               if (dp->width_arg_index != SIZE_MAX)
                  {
                    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 != SIZE_MAX)
                  {
                    if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
                      abort ();
@@ -717,7 +711,10 @@ vasnprintf (char *resultbuf, size_t *len
                                   *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 (allocated + 12,
+                                                          allocated);
+                               if (size_overflow_p (bigger_need))
+                                 goto out_of_memory;
                                ENSURE_ALLOCATION (bigger_need);
                                continue;
                              }
@@ -732,7 +729,6 @@ vasnprintf (char *resultbuf, size_t *len
                      {
                        if (!(result == resultbuf || result == NULL))
                          free (result);
-                       freea (buf);
                        CLEANUP ();
                        errno = EINVAL;
                        return NULL;
@@ -749,13 +745,15 @@ vasnprintf (char *resultbuf, size_t *len
                    if (count >= maxlen)
                      {
                        /* Need at least count bytes.  But allocate
-                          proportionally, to avoid looping eternally if
+                          at least 1 more byte, 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 = xsum (length, count);
+                       size_t allocated2 = xtimes (allocated, 2);
 
+                       if (n < allocated2)
+                         n = allocated2;
+                       if (size_overflow_p (n))
+                         goto out_of_memory;
                        ENSURE_ALLOCATION (n);
 #if HAVE_SNPRINTF
                        continue;
@@ -792,9 +790,15 @@ vasnprintf (char *resultbuf, size_t *len
          result = memory;
       }
 
-    freea (buf);
     CLEANUP ();
     *lengthp = length;
     return result;
+
+  out_of_memory:
+    if (!(result == resultbuf || result == NULL))
+      free (result);
+    CLEANUP ();
+    errno = ENOMEM;
+    return NULL;
   }
 }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]