$NetBSD: patch-aa,v 1.7 2006/08/22 18:43:50 joerg Exp $

--- src/pcf/pcfread.c.orig	2006-01-23 17:35:18.000000000 +0100
+++ src/pcf/pcfread.c
@@ -102,7 +102,8 @@ THE SOFTWARE.
       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 @@ THE SOFTWARE.
       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 @@ THE SOFTWARE.
       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,13 +190,15 @@ THE SOFTWARE.
   }
 
 
+#define PCF_METRIC_SIZE  12
+
   static
   const FT_Frame_Field  pcf_metric_header[] =
   {
 #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 @@ THE SOFTWARE.
 #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,13 +226,15 @@ THE SOFTWARE.
   };
 
 
+#define PCF_COMPRESSED_METRIC_SIZE  5
+
   static
   const FT_Frame_Field  pcf_compressed_metric_header[] =
   {
 #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 @@ THE SOFTWARE.
                ? 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 @@ THE SOFTWARE.
     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,13 +341,15 @@ THE SOFTWARE.
   }
 
 
+#define PCF_PROPERTY_SIZE  9
+
   static
   const FT_Frame_Field  pcf_property_header[] =
   {
 #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 @@ THE SOFTWARE.
 #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 @@ THE SOFTWARE.
                       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 @@ THE SOFTWARE.
 
     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 @@ THE SOFTWARE.
 
     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 @@ THE SOFTWARE.
     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 @@ THE SOFTWARE.
 
       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 @@ THE SOFTWARE.
       }
     }
 
-    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 @@ THE SOFTWARE.
   {
     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 @@ THE SOFTWARE.
     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 @@ THE SOFTWARE.
       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 @@ THE SOFTWARE.
 
       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 @@ THE SOFTWARE.
 
     if ( error )
       FT_FREE( face->metrics );
+
+  Bail:
     return error;
   }
 
@@ -597,14 +688,16 @@ THE SOFTWARE.
     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;
 
     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 @@ THE SOFTWARE.
       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 @@ THE SOFTWARE.
     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 @@ THE SOFTWARE.
 
         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 @@ THE SOFTWARE.
     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 @@ THE SOFTWARE.
       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 @@ THE SOFTWARE.
       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 @@ THE SOFTWARE.
   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;
     }
