Fix buffer overflow in string_vformat. Bug 2449
[exim.git] / src / src / string.c
index d0b8db4ae52bfa834b834c03aed9536364c4438e..3445f8a42ebe678266ae26c2255bc611b47986e6 100644 (file)
@@ -224,6 +224,8 @@ interpreted in strings.
 Arguments:
   pp       points a pointer to the initiating "\" in the string;
            the pointer gets updated to point to the final character
+           If the backslash is the last character in the string, it
+           is not interpreted.
 Returns:   the value of the character escape
 */
 
@@ -236,6 +238,7 @@ const uschar *hex_digits= CUS"0123456789abcdef";
 int ch;
 const uschar *p = *pp;
 ch = *(++p);
+if (ch == '\0') return **pp;
 if (isdigit(ch) && ch != '8' && ch != '9')
   {
   ch -= '0';
@@ -651,18 +654,16 @@ uschar *t, *yield;
 /* First find the end of the string */
 
 if (*s != '\"')
-  {
   while (*s != 0 && !isspace(*s)) s++;
-  }
 else
   {
   s++;
-  while (*s != 0 && *s != '\"')
+  while (*s && *s != '\"')
     {
     if (*s == '\\') (void)string_interpret_escape(&s);
     s++;
     }
-  if (*s != 0) s++;
+  if (*s) s++;
   }
 
 /* Get enough store to copy into */
@@ -907,7 +908,7 @@ int sep = *separator;
 const uschar *s = *listptr;
 BOOL sep_is_special;
 
-if (s == NULL) return NULL;
+if (!s) return NULL;
 
 /* This allows for a fixed specified separator to be an iscntrl() character,
 but at the time of implementation, this is never the case. However, it's best
@@ -923,19 +924,17 @@ if (sep <= 0)
   if (*s == '<' && (ispunct(s[1]) || iscntrl(s[1])))
     {
     sep = s[1];
-    s += 2;
+    if (*++s) ++s;
     while (isspace(*s) && *s != sep) s++;
     }
   else
-    {
-    sep = (sep == 0)? ':' : -sep;
-    }
+    sep = sep ? -sep : ':';
   *separator = sep;
   }
 
 /* An empty string has no list elements */
 
-if (*s == 0) return NULL;
+if (!*s) return NULL;
 
 /* Note whether whether or not the separator is an iscntrl() character. */
 
@@ -946,13 +945,13 @@ sep_is_special = iscntrl(sep);
 if (buffer)
   {
   int p = 0;
-  for (; *s != 0; s++)
+  for (; *s; s++)
     {
     if (*s == sep && (*(++s) != sep || sep_is_special)) break;
     if (p < buflen - 1) buffer[p++] = *s;
     }
   while (p > 0 && isspace(buffer[p-1])) p--;
-  buffer[p] = 0;
+  buffer[p] = '\0';
   }
 
 /* Handle the case when a buffer is not provided. */
@@ -982,10 +981,10 @@ else
 
   for (;;)
     {
-    for (ss = s + 1; *ss != 0 && *ss != sep; ss++) ;
+    for (ss = s + 1; *ss && *ss != sep; ss++) ;
     g = string_catn(g, s, ss-s);
     s = ss;
-    if (*s == 0 || *(++s) != sep || sep_is_special) break;
+    if (!*s || *++s != sep || sep_is_special) break;
     }
   while (g->ptr > 0 && isspace(g->s[g->ptr-1])) g->ptr--;
   buffer = string_from_gstring(g);
@@ -1133,7 +1132,7 @@ store_reset(g->s + (g->size = g->ptr + 1));
 Arguments:
   g            the growable-string
   p            current end of data
-  count                amount to grow by
+  count                amount to grow by, offset from p
 */
 
 static void
@@ -1214,8 +1213,8 @@ memcpy(g->s + p, s, count);
 g->ptr = p + count;
 return g;
 }
+
+
 gstring *
 string_cat(gstring *string, const uschar *s)
 {
@@ -1358,7 +1357,11 @@ while (*fp)
     {
     /* Avoid string_copyn() due to COMPILE_UTILITY */
     if (g->ptr >= lim - 1)
-      if (extend) gstring_grow(g, g->ptr, 1); else return NULL;
+      {
+      if (!extend) return NULL;
+      gstring_grow(g, g->ptr, 1);
+      lim = g->size - 1;
+      }
     g->s[g->ptr++] = (uschar) *fp++;
     continue;
     }
@@ -1426,7 +1429,12 @@ while (*fp)
     case 'X':
       width = length > L_LONG ? 24 : 12;
       if (g->ptr >= lim - width)
-       if (extend) gstring_grow(g, g->ptr, width); else return NULL;
+       {
+       if (!extend) return NULL;
+       gstring_grow(g, g->ptr, width);
+       lim = g->size - 1;
+       gp = CS g->s + g->ptr;
+       }
       strncpy(newformat, item_start, fp - item_start);
       newformat[fp - item_start] = 0;
 
@@ -1451,7 +1459,12 @@ while (*fp)
       {
       void * ptr;
       if (g->ptr >= lim - 24)
-       if (extend) gstring_grow(g, g->ptr, 24); else return NULL;
+       {
+       if (!extend) return NULL;
+       gstring_grow(g, g->ptr, 24);
+       lim = g->size - 1;
+       gp = CS g->s + g->ptr;
+       }
       /* sprintf() saying "(nil)" for a null pointer seems unreliable.
       Handle it explicitly. */
       if ((ptr = va_arg(ap, void *)))
@@ -1479,7 +1492,12 @@ while (*fp)
     case 'G':
       if (precision < 0) precision = 6;
       if (g->ptr >= lim - precision - 8)
-       if (extend) gstring_grow(g, g->ptr, precision+8); else return NULL;
+       {
+       if (!extend) return NULL;
+       gstring_grow(g, g->ptr, precision+8);
+       lim = g->size - 1;
+       gp = CS g->s + g->ptr;
+       }
       strncpy(newformat, item_start, fp - item_start);
       newformat[fp-item_start] = 0;
       if (length == L_LONGDOUBLE)
@@ -1492,13 +1510,21 @@ while (*fp)
 
     case '%':
       if (g->ptr >= lim - 1)
-       if (extend) gstring_grow(g, g->ptr, 1); else return NULL;
+       {
+       if (!extend) return NULL;
+       gstring_grow(g, g->ptr, 1);
+       lim = g->size - 1;
+       }
       g->s[g->ptr++] = (uschar) '%';
       break;
 
     case 'c':
       if (g->ptr >= lim - 1)
-       if (extend) gstring_grow(g, g->ptr, 1); else return NULL;
+       {
+       if (!extend) return NULL;
+       gstring_grow(g, g->ptr, 1);
+       lim = g->size - 1;
+       }
       g->s[g->ptr++] = (uschar) va_arg(ap, int);
       break;
 
@@ -1563,7 +1589,11 @@ while (*fp)
          }
        }
       else if (g->ptr >= lim - width)
+       {
        gstring_grow(g, g->ptr, width);
+       lim = g->size - 1;
+       gp = CS g->s + g->ptr;
+       }
 
       g->ptr += sprintf(gp, "%*.*s", width, precision, s);
       if (fp[-1] == 'S')