* alloc.c: Catch some string size overflows that we were missing.
authorPaul Eggert <eggert@cs.ucla.edu>
Wed, 8 Jun 2011 17:22:24 +0000 (10:22 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Wed, 8 Jun 2011 17:22:24 +0000 (10:22 -0700)
(XMALLOC_OVERRUN_CHECK_SIZE) [!XMALLOC_OVERRUN_CHECK]: Define to 0,
for convenience in STRING_BYTES_MAX.
(STRING_BYTES_MAX): New macro, superseding the old one in lisp.h.
The definition here is exact; the one in lisp.h was approximate.
(allocate_string_data): Check for string overflow.  This catches
some instances we weren't catching before.  Also, it catches
size_t overflow on (unusual) hosts where SIZE_MAX <= min
(PTRDIFF_MAX, MOST_POSITIVE_FIXNUM), e.g., when size_t is 32 bits
and ptrdiff_t and EMACS_INT are both 64 bits.
* character.c, coding.c, doprnt.c, editfns.c, eval.c:
All uses of STRING_BYTES_MAX replaced by STRING_BYTES_BOUND.
* lisp.h (STRING_BYTES_BOUND): Renamed from STRING_BYTES_MAX.

src/ChangeLog
src/alloc.c
src/character.c
src/coding.c
src/doprnt.c
src/editfns.c
src/eval.c
src/lisp.h

index b05b360..64f346b 100644 (file)
@@ -1,3 +1,19 @@
+2011-06-08  Paul Eggert  <eggert@cs.ucla.edu>
+
+       * alloc.c: Catch some string size overflows that we were missing.
+       (XMALLOC_OVERRUN_CHECK_SIZE) [!XMALLOC_OVERRUN_CHECK]: Define to 0,
+       for convenience in STRING_BYTES_MAX.
+       (STRING_BYTES_MAX): New macro, superseding the old one in lisp.h.
+       The definition here is exact; the one in lisp.h was approximate.
+       (allocate_string_data): Check for string overflow.  This catches
+       some instances we weren't catching before.  Also, it catches
+       size_t overflow on (unusual) hosts where SIZE_MAX <= min
+       (PTRDIFF_MAX, MOST_POSITIVE_FIXNUM), e.g., when size_t is 32 bits
+       and ptrdiff_t and EMACS_INT are both 64 bits.
+       * character.c, coding.c, doprnt.c, editfns.c, eval.c:
+       All uses of STRING_BYTES_MAX replaced by STRING_BYTES_BOUND.
+       * lisp.h (STRING_BYTES_BOUND): Renamed from STRING_BYTES_MAX.
+
 2011-06-07  Paul Eggert  <eggert@cs.ucla.edu>
 
        * character.c (string_escape_byte8): Fix nbytes/nchars typo.
index db1744b..fa4f1d3 100644 (file)
@@ -485,7 +485,9 @@ buffer_memory_full (EMACS_INT nbytes)
 }
 
 
-#ifdef XMALLOC_OVERRUN_CHECK
+#ifndef XMALLOC_OVERRUN_CHECK
+#define XMALLOC_OVERRUN_CHECK_SIZE 0
+#else
 
 /* Check for overrun in malloc'ed buffers by wrapping a 16 byte header
    and a 16 byte trailer around each block.
@@ -1659,6 +1661,18 @@ static char const string_overrun_cookie[GC_STRING_OVERRUN_COOKIE_SIZE] =
 
 #define GC_STRING_EXTRA (GC_STRING_OVERRUN_COOKIE_SIZE)
 
+/* Exact bound on the number of bytes in a string, not counting the
+   terminating null.  A string cannot contain more bytes than
+   STRING_BYTES_BOUND, nor can it be so long that the size_t
+   arithmetic in allocate_string_data would overflow while it is
+   calculating a value to be passed to malloc.  */
+#define STRING_BYTES_MAX                                         \
+  min (STRING_BYTES_BOUND,                                       \
+       ((SIZE_MAX - XMALLOC_OVERRUN_CHECK_SIZE - GC_STRING_EXTRA  \
+        - offsetof (struct sblock, first_data)                   \
+        - SDATA_DATA_OFFSET)                                     \
+       & ~(sizeof (EMACS_INT) - 1)))
+
 /* Initialize string allocation.  Called from init_alloc_once.  */
 
 static void
@@ -1858,6 +1872,9 @@ allocate_string_data (struct Lisp_String *s,
   struct sblock *b;
   EMACS_INT needed, old_nbytes;
 
+  if (STRING_BYTES_MAX < nbytes)
+    string_overflow ();
+
   /* Determine the number of bytes needed to store NBYTES bytes
      of string data.  */
   needed = SDATA_SIZE (nbytes);
index fe8be70..aae3e3c 100644 (file)
@@ -838,7 +838,7 @@ string_escape_byte8 (Lisp_Object string)
   if (multibyte)
     {
       if ((MOST_POSITIVE_FIXNUM - nchars) / 3 < byte8_count
-         || (STRING_BYTES_MAX - nbytes) / 2 < byte8_count)
+         || (STRING_BYTES_BOUND - nbytes) / 2 < byte8_count)
        string_overflow ();
 
       /* Convert 2-byte sequence of byte8 chars to 4-byte octal.  */
@@ -847,7 +847,7 @@ string_escape_byte8 (Lisp_Object string)
     }
   else
     {
-      if ((STRING_BYTES_MAX - nbytes) / 3 < byte8_count)
+      if ((STRING_BYTES_BOUND - nbytes) / 3 < byte8_count)
        string_overflow ();
 
       /* Convert 1-byte sequence of byte8 chars to 4-byte octal.  */
index 64e8e41..d914e0d 100644 (file)
@@ -1071,7 +1071,7 @@ coding_set_destination (struct coding_system *coding)
 static void
 coding_alloc_by_realloc (struct coding_system *coding, EMACS_INT bytes)
 {
-  if (STRING_BYTES_MAX - coding->dst_bytes < bytes)
+  if (STRING_BYTES_BOUND - coding->dst_bytes < bytes)
     string_overflow ();
   coding->destination = (unsigned char *) xrealloc (coding->destination,
                                                    coding->dst_bytes + bytes);
index 5ca3ea8..f5e3115 100644 (file)
@@ -329,7 +329,7 @@ doprnt (char *buffer, register size_t bufsize, const char *format,
                minlen = atoi (&fmtcpy[1]);
              string = va_arg (ap, char *);
              tem = strlen (string);
-             if (tem > STRING_BYTES_MAX)
+             if (STRING_BYTES_BOUND < tem)
                error ("String for %%s or %%S format is too long");
              width = strwidth (string, tem);
              goto doit1;
@@ -338,7 +338,7 @@ doprnt (char *buffer, register size_t bufsize, const char *format,
            doit:
              /* Coming here means STRING contains ASCII only.  */
              tem = strlen (string);
-             if (tem > STRING_BYTES_MAX)
+             if (STRING_BYTES_BOUND < tem)
                error ("Format width or precision too large");
              width = tem;
            doit1:
index b4ce9a1..232af35 100644 (file)
@@ -3589,7 +3589,7 @@ usage: (format STRING &rest OBJECTS)  */)
   char initial_buffer[4000];
   char *buf = initial_buffer;
   EMACS_INT bufsize = sizeof initial_buffer;
-  EMACS_INT max_bufsize = STRING_BYTES_MAX + 1;
+  EMACS_INT max_bufsize = STRING_BYTES_BOUND + 1;
   char *p;
   Lisp_Object buf_save_value IF_LINT (= {0});
   register char *format, *end, *format_start;
index ef5abac..a9703fc 100644 (file)
@@ -1994,7 +1994,7 @@ verror (const char *m, va_list ap)
 {
   char buf[4000];
   size_t size = sizeof buf;
-  size_t size_max = STRING_BYTES_MAX + 1;
+  size_t size_max = STRING_BYTES_BOUND + 1;
   size_t mlen = strlen (m);
   char *buffer = buf;
   size_t used;
index c5f810a..a1bc794 100644 (file)
@@ -765,11 +765,19 @@ extern EMACS_INT string_bytes (struct Lisp_String *);
 
 #endif /* not GC_CHECK_STRING_BYTES */
 
-/* A string cannot contain more bytes than a fixnum can represent,
-   nor can it be so long that C pointer arithmetic stops working on
-   the string plus a terminating null.  */
-#define STRING_BYTES_MAX  \
-  min (MOST_POSITIVE_FIXNUM, min (SIZE_MAX, PTRDIFF_MAX) - 1)
+/* An upper bound on the number of bytes in a Lisp string, not
+   counting the terminating null.  This a tight enough bound to
+   prevent integer overflow errors that would otherwise occur during
+   string size calculations.  A string cannot contain more bytes than
+   a fixnum can represent, nor can it be so long that C pointer
+   arithmetic stops working on the string plus its terminating null.
+   Although the actual size limit (see STRING_BYTES_MAX in alloc.c)
+   may be a bit smaller than STRING_BYTES_BOUND, calculating it here
+   would expose alloc.c internal details that we'd rather keep
+   private.  The cast to ptrdiff_t ensures that STRING_BYTES_BOUND is
+   signed.  */
+#define STRING_BYTES_BOUND  \
+  min (MOST_POSITIVE_FIXNUM, (ptrdiff_t) min (SIZE_MAX, PTRDIFF_MAX) - 1)
 
 /* Mark STR as a unibyte string.  */
 #define STRING_SET_UNIBYTE(STR)  \