Commit 0d0365ec59a68767214b357f35379e1dbd3929e9

David Turner 2007-05-22T09:53:44

avoid heap explosion in the case of malformed .Z font files related to bug #19910, but not a bugfix yet

diff --git a/ChangeLog b/ChangeLog
index 19c96dc..3f044a8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,9 +1,15 @@
-2007-06-20  Ismail Dönmez  <ismail@pardus.org.tr>
+2007-05-22  David Turner  <david@freetype.org>
+
+	* src/lzw/ftzopen.h, src/lzw/ftzopen.c: apply some "band-aid"
+	to avoid blowing up the heap in the case of malformed fonts.
+	related to bug #19910; *not* a real fix though...
+
+2007-05-20  Ismail Dönmez  <ismail@pardus.org.tr>
 
 	* src/pshinter/pshrec.c (ps_mask_table_set_bits): Add `const'.
 	(ps_dimension_set_mask_bits): Remove `const'.
 
-2007-06-19  Werner Lemberg  <wl@gnu.org>
+2007-05-19  Werner Lemberg  <wl@gnu.org>
 
 	* src/sfnt/ttmtx.c (tt_face_get_metrics)
 	[!FT_CONFIG_OPTION_OLD_INTERNALS]: Another type-punning fix.
diff --git a/src/lzw/ftzopen.c b/src/lzw/ftzopen.c
index a9d25c5..f486d1b 100644
--- a/src/lzw/ftzopen.c
+++ b/src/lzw/ftzopen.c
@@ -87,6 +87,11 @@
       FT_UInt    old_size = state->stack_size;
       FT_UInt    new_size = old_size;
 
+      /* limit stack size to detect invalid files                           */
+      /* the magic number comes from the origin NetBSD zopen implementation */
+      if (state->stack_size >= 69001 )
+          return -1;
+
       new_size = new_size + ( new_size >> 1 ) + 4;
 
       if ( state->stack == state->stack_0 )
@@ -110,6 +115,7 @@
   {
     FT_UInt    old_size = state->prefix_size;
     FT_UInt    new_size = old_size;
+    FT_UInt    max_size = INT_MAX/(sizeof(FT_UShort)+sizeof(FT_Byte));
     FT_Memory  memory   = state->memory;
     FT_Error   error;
 
@@ -119,6 +125,13 @@
     else
       new_size += new_size >> 2;  /* don't grow too fast */
 
+    if (new_size < old_size || new_size > max_size)
+    {
+        new_size = max_size;
+        if (new_size == old_size)
+            return -1;
+    }
+
     /*
      *  Note that the `suffix' array is located in the same memory block
      *  pointed to by `prefix'.
@@ -213,11 +226,11 @@
   {
     FT_ULong  result = 0;
 
-    FT_UInt  num_bits = state->num_bits;
-    FT_UInt  free_ent = state->free_ent;
-    FT_UInt  old_char = state->old_char;
-    FT_UInt  old_code = state->old_code;
-    FT_UInt  in_code  = state->in_code;
+    FT_UInt  num_bits  = state->num_bits;
+    FT_UInt  free_ent  = state->free_ent;
+    FT_UInt  last_char = state->last_char;
+    FT_UInt  old_code  = state->old_code;
+    FT_UInt  in_code   = state->in_code;
 
 
     if ( out_size == 0 )
@@ -255,15 +268,15 @@
         if ( c < 0 )
           goto Eof;
 
-        old_code = old_char = (FT_UInt)c;
+        old_code = last_char = (FT_UInt)c;
 
         if ( buffer )
-          buffer[result] = (FT_Byte)old_char;
+          buffer[result] = (FT_Byte)last_char;
+
+        state->phase = FT_LZW_PHASE_CODE;
 
         if ( ++result >= out_size )
           goto Exit;
-
-        state->phase = FT_LZW_PHASE_CODE;
       }
       /* fall-through */
 
@@ -303,7 +316,7 @@
           /* special case for KwKwKwK */
           if ( code - 256U >= free_ent )
           {
-            FTLZW_STACK_PUSH( old_char );
+            FTLZW_STACK_PUSH( last_char );
             code = old_code;
           }
 
@@ -314,8 +327,8 @@
           }
         }
 
-        old_char = code;
-        FTLZW_STACK_PUSH( old_char );
+        last_char = code;
+        FTLZW_STACK_PUSH( last_char );
 
         state->phase = FT_LZW_PHASE_STACK;
       }
@@ -344,7 +357,7 @@
           FT_ASSERT( free_ent < state->prefix_size );
 
           state->prefix[free_ent] = (FT_UShort)old_code;
-          state->suffix[free_ent] = (FT_Byte)  old_char;
+          state->suffix[free_ent] = (FT_Byte)  last_char;
 
           if ( ++free_ent == state->free_bits )
           {
@@ -367,11 +380,11 @@
     }
 
   Exit:
-    state->num_bits = num_bits;
-    state->free_ent = free_ent;
-    state->old_code = old_code;
-    state->old_char = old_char;
-    state->in_code  = in_code;
+    state->num_bits  = num_bits;
+    state->free_ent  = free_ent;
+    state->old_code  = old_code;
+    state->last_char = last_char;
+    state->in_code   = in_code;
 
     return result;
 
diff --git a/src/lzw/ftzopen.h b/src/lzw/ftzopen.h
index 8b1831b..220c1cc 100644
--- a/src/lzw/ftzopen.h
+++ b/src/lzw/ftzopen.h
@@ -128,7 +128,7 @@
     FT_UInt      free_ent;    /* index of next free entry */
     FT_UInt      free_bits;   /* if reached by free_ent, increment num_bits */
     FT_UInt      old_code;
-    FT_UInt      old_char;
+    FT_UInt      last_char;
     FT_UInt      in_code;
 
     FT_UShort*   prefix;      /* always dynamically allocated / reallocated */