shithub: freetype+ttf2subf

Download patch

ref: 4af3c4d7ee1de7d4e7591578092cac4f1a124d49
parent: b97daa286191ae31e7d7259fa4844c38b0e0623a
author: Werner Lemberg <[email protected]>
date: Sat Jul 8 17:31:24 EDT 2006

Add many checks to protect against malformed PCF files.

* src/pcf/pcfdrivr.c (PCF_Face_Done): Protect against NULL pointers.
(PCF_Face_Init): Add calls to PCF_Face_Done in case of errors.

* src/pcf/pcfread.c (pcf_read_TOC): Protect against malformed table
data and check that tables don't overlap (using a simple
bubblesort).
(PCF_METRIC_SIZE, PCF_COMPRESSED_METRIC_SIZE, PCF_PROPERTY_SIZE):
New macros which give the size of data structures in the data
stream.
(pcf_get_properties): Use rough estimates to get array size limits.
Assign `face->nprops' and `face->properties' earlier so that a call
to PCF_Face_Done can do the clean-up in case of error.
Protect against invalid string offsets.
(pcf_get_metrics): Clean up code.
Adjust tracing message levels.
Use rough estimate to get array size limit.
(pcf_get_bitmaps): Clean up code.
Adjust tracing message levels.
Use rough estimates to get offset limits.
(pcf_get_encodings): Adjust tracing message level.
(pcf_get_accel): Clean up code.

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2006-07-07  Werner Lemberg  <[email protected]>
+
+	Add many checks to protect against malformed PCF files.
+
+	* src/pcf/pcfdrivr.c (PCF_Face_Done): Protect against NULL pointers.
+	(PCF_Face_Init): Add calls to PCF_Face_Done in case of errors.
+
+	* src/pcf/pcfread.c (pcf_read_TOC): Protect against malformed table
+	data and check that tables don't overlap (using a simple
+	bubblesort).
+	(PCF_METRIC_SIZE, PCF_COMPRESSED_METRIC_SIZE, PCF_PROPERTY_SIZE):
+	New macros which give the size of data structures in the data
+	stream.
+	(pcf_get_properties): Use rough estimates to get array size limits.
+	Assign `face->nprops' and `face->properties' earlier so that a call
+	to PCF_Face_Done can do the clean-up in case of error.
+	Protect against invalid string offsets.
+	(pcf_get_metrics): Clean up code.
+	Adjust tracing message levels.
+	Use rough estimate to get array size limit.
+	(pcf_get_bitmaps): Clean up code.
+	Adjust tracing message levels.
+	Use rough estimates to get offset limits.
+	(pcf_get_encodings): Adjust tracing message level.
+	(pcf_get_accel): Clean up code.
+
 2006-06-26  Werner Lemberg  <[email protected]>
 
 	* src/bdf/bdfdrivr.c (BDF_Face_Init): Handle fonts correctly which
--- a/src/pcf/pcfdrivr.c
+++ b/src/pcf/pcfdrivr.c
@@ -203,19 +203,23 @@
 
     /* free properties */
     {
-      PCF_Property  prop = face->properties;
+      PCF_Property  prop;
       FT_Int        i;
 
 
-      for ( i = 0; i < face->nprops; i++ )
+      if ( face->properties )
       {
-        prop = &face->properties[i];
+        for ( i = 0; i < face->nprops; i++ )
+        {
+          prop = &face->properties[i];
 
-        FT_FREE( prop->name );
-        if ( prop->isString )
-          FT_FREE( prop->value.atom );
+          if ( prop ) {
+            FT_FREE( prop->name );
+            if ( prop->isString )
+              FT_FREE( prop->value.atom );
+          }
+        }
       }
-
       FT_FREE( face->properties );
     }
 
@@ -258,6 +262,8 @@
       FT_Error  error2;
 
 
+      PCF_Face_Done( pcfface );
+
       /* this didn't work, try gzip support! */
       error2 = FT_Stream_OpenGzip( &face->gzip_stream, stream );
       if ( FT_ERROR_BASE( error2 ) == FT_Err_Unimplemented_Feature )
@@ -357,6 +363,7 @@
 
   Fail:
     FT_TRACE2(( "[not a valid PCF file]\n" ));
+    PCF_Face_Done( pcfface );
     error = PCF_Err_Unknown_File_Format;  /* error */
     goto Exit;
   }
--- a/src/pcf/pcfread.c
+++ b/src/pcf/pcfread.c
@@ -102,7 +102,8 @@
       return PCF_Err_Cannot_Open_Resource;
 
     if ( toc->version != PCF_FILE_VERSION                 ||
-         toc->count   >  FT_ARRAY_MAX( face->toc.tables ) )
+         toc->count   >  FT_ARRAY_MAX( face->toc.tables ) ||
+         toc->count   == 0                                )
       return PCF_Err_Invalid_File_Format;
 
     if ( FT_NEW_ARRAY( face->toc.tables, toc->count ) )
@@ -116,6 +117,41 @@
       tables++;
     }
 
+    /* Sort tables and check for overlaps.  Because they are almost      */
+    /* always ordered already, an in-place bubble sort with simultaneous */
+    /* boundary checking seems appropriate.                              */
+    tables = face->toc.tables;
+
+    for ( n = 0; n < toc->count - 1; n++ )
+    {
+      FT_UInt  i, have_change;
+
+
+      have_change = 0;
+
+      for ( i = 0; i < toc->count - 1 - n; i++ )
+      {
+        PCF_TableRec  tmp;
+
+
+        if ( tables[i].offset > tables[i + 1].offset )
+        {
+          tmp           = tables[i];
+          tables[i]     = tables[i + 1];
+          tables[i + 1] = tmp;
+
+          have_change = 1;
+        }
+
+        if ( ( tables[i].size   > tables[i + 1].offset )                  ||
+             ( tables[i].offset > tables[i + 1].offset - tables[i].size ) )
+          return PCF_Err_Invalid_Offset;
+      }
+
+      if ( !have_change )
+        break;
+    }
+
 #if defined( FT_DEBUG_LEVEL_TRACE )
 
     {
@@ -130,7 +166,8 @@
       tables = face->toc.tables;
       for ( i = 0; i < toc->count; i++ )
       {
-        for( j = 0; j < sizeof ( tableNames ) / sizeof ( tableNames[0] ); j++ )
+        for ( j = 0; j < sizeof ( tableNames ) / sizeof ( tableNames[0] );
+              j++ )
           if ( tables[i].type == (FT_UInt)( 1 << j ) )
             name = tableNames[j];
 
@@ -153,6 +190,8 @@
   }
 
 
+#define PCF_METRIC_SIZE  12
+
   static
   const FT_Frame_Field  pcf_metric_header[] =
   {
@@ -159,7 +198,7 @@
 #undef  FT_STRUCTURE
 #define FT_STRUCTURE  PCF_MetricRec
 
-    FT_FRAME_START( 12 ),
+    FT_FRAME_START( PCF_METRIC_SIZE ),
       FT_FRAME_SHORT_LE( leftSideBearing ),
       FT_FRAME_SHORT_LE( rightSideBearing ),
       FT_FRAME_SHORT_LE( characterWidth ),
@@ -176,7 +215,7 @@
 #undef  FT_STRUCTURE
 #define FT_STRUCTURE  PCF_MetricRec
 
-    FT_FRAME_START( 12 ),
+    FT_FRAME_START( PCF_METRIC_SIZE ),
       FT_FRAME_SHORT( leftSideBearing ),
       FT_FRAME_SHORT( rightSideBearing ),
       FT_FRAME_SHORT( characterWidth ),
@@ -187,6 +226,8 @@
   };
 
 
+#define PCF_COMPRESSED_METRIC_SIZE  5
+
   static
   const FT_Frame_Field  pcf_compressed_metric_header[] =
   {
@@ -193,7 +234,7 @@
 #undef  FT_STRUCTURE
 #define FT_STRUCTURE  PCF_Compressed_MetricRec
 
-    FT_FRAME_START( 5 ),
+    FT_FRAME_START( PCF_COMPRESSED_METRIC_SIZE ),
       FT_FRAME_BYTE( leftSideBearing ),
       FT_FRAME_BYTE( rightSideBearing ),
       FT_FRAME_BYTE( characterWidth ),
@@ -221,7 +262,7 @@
                ? pcf_metric_msb_header
                : pcf_metric_header;
 
-      /* the following sets 'error' but doesn't return in case of failure */
+      /* the following sets `error' but doesn't return in case of failure */
       (void)FT_STREAM_READ_FIELDS( fields, metric );
     }
     else
@@ -261,17 +302,19 @@
     for ( i = 0; i < ntables; i++ )
       if ( tables[i].type == type )
       {
-        if ( stream->pos > tables[i].offset ) {
+        if ( stream->pos > tables[i].offset )
+        {
           error = PCF_Err_Invalid_Stream_Skip;
           goto Fail;
         }
 
-        if ( FT_STREAM_SKIP( tables[i].offset - stream->pos ) ) {
+        if ( FT_STREAM_SKIP( tables[i].offset - stream->pos ) )
+        {
           error = PCF_Err_Invalid_Stream_Skip;
           goto Fail;
         }
 
-        *asize   = tables[i].size;  /* unused - to be removed */
+        *asize   = tables[i].size;
         *aformat = tables[i].format;
 
         return PCF_Err_Ok;
@@ -298,6 +341,8 @@
   }
 
 
+#define PCF_PROPERTY_SIZE  9
+
   static
   const FT_Frame_Field  pcf_property_header[] =
   {
@@ -304,7 +349,7 @@
 #undef  FT_STRUCTURE
 #define FT_STRUCTURE  PCF_ParsePropertyRec
 
-    FT_FRAME_START( 9 ),
+    FT_FRAME_START( PCF_PROPERTY_SIZE ),
       FT_FRAME_LONG_LE( name ),
       FT_FRAME_BYTE   ( isString ),
       FT_FRAME_LONG_LE( value ),
@@ -318,7 +363,7 @@
 #undef  FT_STRUCTURE
 #define FT_STRUCTURE  PCF_ParsePropertyRec
 
-    FT_FRAME_START( 9 ),
+    FT_FRAME_START( PCF_PROPERTY_SIZE ),
       FT_FRAME_LONG( name ),
       FT_FRAME_BYTE( isString ),
       FT_FRAME_LONG( value ),
@@ -353,8 +398,8 @@
                       PCF_Face   face )
   {
     PCF_ParseProperty  props      = 0;
-    PCF_Property       properties = 0;
-    FT_Int             nprops, i;
+    PCF_Property       properties;
+    FT_UInt            nprops, i;
     FT_ULong           format, size;
     FT_Error           error;
     FT_Memory          memory     = FT_FACE(face)->memory;
@@ -390,6 +435,15 @@
 
     FT_TRACE4(( "  nprop = %d\n", nprops ));
 
+    /* rough estimate */
+    if ( nprops > size / PCF_PROPERTY_SIZE )
+    {
+      error = PCF_Err_Invalid_Table;
+      goto Bail;
+    }
+
+    face->nprops = nprops;
+
     if ( FT_NEW_ARRAY( props, nprops ) )
       goto Bail;
 
@@ -427,6 +481,13 @@
 
     FT_TRACE4(( "  string_size = %ld\n", string_size ));
 
+    /* rough estimate */
+    if ( string_size > size - nprops * PCF_PROPERTY_SIZE )
+    {
+      error = PCF_Err_Invalid_Table;
+      goto Bail;
+    }
+
     if ( FT_NEW_ARRAY( strings, string_size ) )
       goto Bail;
 
@@ -437,13 +498,24 @@
     if ( FT_NEW_ARRAY( properties, nprops ) )
       goto Bail;
 
+    face->properties = properties;
+
     for ( i = 0; i < nprops; i++ )
     {
-      /* XXX: make atom */
+      FT_Long  name_offset = props[i].name;
+
+
+      if ( ( name_offset < 0 )                     ||
+           ( (FT_ULong)name_offset > string_size ) )
+      {
+        error = PCF_Err_Invalid_Offset;
+        goto Bail;
+      }
+
       if ( FT_NEW_ARRAY( properties[i].name,
-                         ft_strlen( strings + props[i].name ) + 1 ) )
+                         ft_strlen( strings + name_offset ) + 1 ) )
         goto Bail;
-      ft_strcpy( properties[i].name, strings + props[i].name );
+      ft_strcpy( properties[i].name, strings + name_offset );
 
       FT_TRACE4(( "  %s:", properties[i].name ));
 
@@ -451,8 +523,18 @@
 
       if ( props[i].isString )
       {
+        FT_Long  value_offset = props[i].value;
+
+
+        if ( ( value_offset < 0 )                     ||
+             ( (FT_ULong)value_offset > string_size ) )
+        {
+          error = PCF_Err_Invalid_Offset;
+          goto Bail;
+        }
+
         if ( FT_NEW_ARRAY( properties[i].value.atom,
-                           ft_strlen( strings + props[i].value ) + 1 ) )
+                           ft_strlen( strings + value_offset ) + 1 ) )
           goto Bail;
         ft_strcpy( properties[i].value.atom, strings + props[i].value );
 
@@ -466,14 +548,8 @@
       }
     }
 
-    face->properties = properties;
-    face->nprops = nprops;
-
-    FT_FREE( props );
-    FT_FREE( strings );
-
-    return PCF_Err_Ok;
-
+    error = PCF_Err_Ok;
+    
   Bail:
     FT_FREE( props );
     FT_FREE( strings );
@@ -488,11 +564,9 @@
   {
     FT_Error    error    = PCF_Err_Ok;
     FT_Memory   memory   = FT_FACE(face)->memory;
-    FT_ULong    format   = 0;
-    FT_ULong    size     = 0;
+    FT_ULong    format, size;
     PCF_Metric  metrics  = 0;
-    int         i;
-    int         nmetrics = -1;
+    FT_ULong    nmetrics, i;
 
 
     error = pcf_seek_to_table_type( stream,
@@ -504,7 +578,8 @@
     if ( error )
       return error;
 
-    error = FT_READ_ULONG_LE( format );
+    if ( FT_READ_ULONG_LE( format ) )
+      goto Bail;
 
     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT )     &&
          !PCF_FORMAT_MATCH( format, PCF_COMPRESSED_METRICS ) )
@@ -524,16 +599,30 @@
       else
         (void)FT_READ_USHORT_LE( nmetrics );
     }
-    if ( error || nmetrics == -1 )
+    if ( error )
       return PCF_Err_Invalid_File_Format;
 
     face->nmetrics = nmetrics;
 
+    FT_TRACE4(( "pcf_get_metrics:\n" ));
+
+    FT_TRACE4(( "  number of metrics: %d\n", nmetrics ));
+
+    /* rough estimate */
+    if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
+    {
+      if ( nmetrics > size / PCF_METRIC_SIZE )
+        return PCF_Err_Invalid_Table;
+    }
+    else
+    {
+      if ( nmetrics > size / PCF_COMPRESSED_METRIC_SIZE )
+        return PCF_Err_Invalid_Table;
+    }
+
     if ( FT_NEW_ARRAY( face->metrics, nmetrics ) )
       return PCF_Err_Out_Of_Memory;
 
-    FT_TRACE4(( "pcf_get_metrics:\n" ));
-
     metrics = face->metrics;
     for ( i = 0; i < nmetrics; i++ )
     {
@@ -541,7 +630,7 @@
 
       metrics[i].bits = 0;
 
-      FT_TRACE4(( "  idx %d: width=%d, "
+      FT_TRACE5(( "  idx %d: width=%d, "
                   "lsb=%d, rsb=%d, ascent=%d, descent=%d, swidth=%d\n",
                   i,
                   ( metrics + i )->characterWidth,
@@ -557,6 +646,8 @@
 
     if ( error )
       FT_FREE( face->metrics );
+
+  Bail:
     return error;
   }
 
@@ -597,6 +688,10 @@
     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
       return PCF_Err_Invalid_File_Format;
 
+    FT_TRACE4(( "pcf_get_bitmaps:\n" ));
+
+    FT_TRACE4(( "  number of bitmaps: %d\n", nbitmaps ));
+
     if ( nbitmaps != face->nmetrics )
       return PCF_Err_Invalid_File_Format;
 
@@ -603,8 +698,6 @@
     if ( FT_NEW_ARRAY( offsets, nbitmaps ) )
       return error;
 
-    FT_TRACE4(( "pcf_get_bitmaps:\n" ));
-
     for ( i = 0; i < nbitmaps; i++ )
     {
       if ( PCF_BYTE_ORDER( format ) == MSBFirst )
@@ -612,7 +705,7 @@
       else
         (void)FT_READ_LONG_LE( offsets[i] );
 
-      FT_TRACE4(( "  bitmap %d: offset %ld (0x%lX)\n",
+      FT_TRACE5(( "  bitmap %d: offset %ld (0x%lX)\n",
                   i, offsets[i], offsets[i] ));
     }
     if ( error )
@@ -640,15 +733,22 @@
     FT_UNUSED( sizebitmaps );       /* only used for debugging */
 
     for ( i = 0; i < nbitmaps; i++ )
-      face->metrics[i].bits = stream->pos + offsets[i];
+    {
+      /* rough estimate */
+      if ( ( offsets[i] < 0 )              ||
+           ( (FT_ULong)offsets[i] > size ) )
+      {
+        FT_ERROR(( "pcf_get_bitmaps:"));
+        FT_ERROR(( " invalid offset to bitmap data of glyph %d\n", i ));
+      }
+      else
+        face->metrics[i].bits = stream->pos + offsets[i];
+    }
 
     face->bitmapsFormat = format;
 
-    FT_FREE ( offsets );
-    return error;
-
   Bail:
-    FT_FREE ( offsets );
+    FT_FREE( offsets );
     return error;
   }
 
@@ -734,7 +834,7 @@
 
         tmpEncoding[j].glyph = (FT_Short)encodingOffset;
 
-        FT_TRACE4(( "  code %d (0x%04X): idx %d\n",
+        FT_TRACE5(( "  code %d (0x%04X): idx %d\n",
                     tmpEncoding[j].enc, tmpEncoding[j].enc,
                     tmpEncoding[j].glyph ));
 
@@ -828,7 +928,8 @@
     if ( error )
       goto Bail;
 
-    error = FT_READ_ULONG_LE( format );
+    if ( FT_READ_ULONG_LE( format ) )
+      goto Bail;
 
     if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT )    &&
          !PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) )
@@ -876,7 +977,6 @@
       accel->ink_minbounds = accel->minbounds; /* I'm not sure about this */
       accel->ink_maxbounds = accel->maxbounds;
     }
-    return error;
 
   Bail:
     return error;
@@ -1082,11 +1182,12 @@
       else
         root->family_name = NULL;
 
-      /* Note: We shift all glyph indices by +1 since we must
+      /* 
+       * Note: We shift all glyph indices by +1 since we must
        * respect the convention that glyph 0 always corresponds
-       * to the "missing glyph".
+       * to the `missing glyph'.
        *
-       * This implies bumping the number of "available" glyphs by 1.
+       * This implies bumping the number of `available' glyphs by 1.
        */
       root->num_glyphs = face->nmetrics + 1;
 
@@ -1171,7 +1272,7 @@
   Exit:
     if ( error )
     {
-      /* this is done to respect the behaviour of the original */
+      /* This is done to respect the behaviour of the original */
       /* PCF font driver.                                      */
       error = PCF_Err_Invalid_File_Format;
     }