Commit fba29fabb3c3892ccd8afc3a9c3988aaeb036dc8

Werner Lemberg 2015-10-27T21:04:48

[pfr] Add some safety guards (#46302). * src/pfr/pfrload.h (PFR_CHECK): Rename to... (PFR_CHECK_SIZE): ... this. (PFR_SIZE): [!PFR_CONFIG_NO_CHECKS]: Define to PFR_CHECK_SIZE. * src/pfr/pfrload.c (pfr_log_font_count): Check `count'. (pfr_extra_item_load_kerning_pairs): Remove tracing message. (pfr_phy_font_load): Use PFR_CHECK_SIZE where appropriate. Allocate `chars' after doing a size checks. * src/pfr/pfrsbit.c (pfr_load_bitmap_bits): Move test for invalid bitmap format to... (pfr_slot_load_bitmap): ... this function. Check bitmap size.

diff --git a/ChangeLog b/ChangeLog
index a19acf0..8c618cc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2015-10-27  Werner Lemberg  <wl@gnu.org>
+
+	[pfr] Add some safety guards (#46302).
+
+	* src/pfr/pfrload.h (PFR_CHECK): Rename to...
+	(PFR_CHECK_SIZE): ... this.
+	(PFR_SIZE): [!PFR_CONFIG_NO_CHECKS]: Define to PFR_CHECK_SIZE.
+
+	* src/pfr/pfrload.c (pfr_log_font_count): Check `count'. 
+	(pfr_extra_item_load_kerning_pairs): Remove tracing message.
+	(pfr_phy_font_load): Use PFR_CHECK_SIZE where appropriate.
+	Allocate `chars' after doing a size checks.
+
+	* src/pfr/pfrsbit.c (pfr_load_bitmap_bits): Move test for invalid
+	bitmap format to...
+	(pfr_slot_load_bitmap): ... this function.
+	Check bitmap size.
+
 2015-10-26  Werner Lemberg  <wl@gnu.org>
 
 	[truetype] Fix sanitizing logic for `loca' (#46223).
diff --git a/src/pfr/pfrload.c b/src/pfr/pfrload.c
index a1cae2c..ed8327a 100644
--- a/src/pfr/pfrload.c
+++ b/src/pfr/pfrload.c
@@ -26,6 +26,93 @@
 #define FT_COMPONENT  trace_pfr
 
 
+  /*
+   *  The overall structure of a PFR file is as follows.
+   *
+   *    PFR header
+   *      58 bytes (contains nPhysFonts)
+   *
+   *    Logical font directory (size at most 2^16 bytes)
+   *      2 bytes (nLogFonts)
+   *      + nLogFonts * 5 bytes
+   *
+   *         ==>   nLogFonts <= 13106
+   *
+   *    Logical font section (size at most 2^24 bytes)
+   *      nLogFonts * logFontRecord
+   *
+   *      logFontRecord (size at most 2^16 bytes)
+   *        12 bytes (fontMatrix)
+   *        + 1 byte (flags)
+   *        + 0-5 bytes (depending on `flags')
+   *        + 0-(1+255*(2+255)) = 0-65536 (depending on `flags')
+   *        + 5 bytes (physical font info)
+   *        + 0-1 bytes (depending on PFR header)
+   *
+   *         ==>   minimum size 18 bytes
+   *
+   *    Physical font section (size at most 2^24 bytes)
+   *      nPhysFonts * (physFontRecord
+   *                    + nBitmapSizes * nBmapChars * bmapCharRecord)
+   *
+   *      physFontRecord (size at most 2^24 bytes)
+   *        14 bytes (font info)
+   *        + 1 byte (flags)
+   *        + 0-2 (depending on `flags')
+   *        + 0-? (structure too complicated to be shown here; depending on
+   *               `flags'; contains `nBitmapSizes' and `nBmapChars')
+   *        + 3 bytes (nAuxBytes)
+   *        + nAuxBytes
+   *        + 1 byte (nBlueValues)
+   *        + 2 * nBlueValues
+   *        + 6 bytes (hinting data)
+   *        + 2 bytes (nCharacters)
+   *        + nCharacters * (4-10 bytes) (depending on `flags')
+   *
+   *         ==>   minimum size 27 bytes
+   *
+   *      bmapCharRecord
+   *        4-7 bytes
+   *
+   *    Glyph program strings (three possible types: simpleGps, compoundGps,
+   *                           and bitmapGps; size at most 2^24 bytes)
+   *      simpleGps (size at most 2^16 bytes)
+   *        1 byte (flags)
+   *        1-2 bytes (n[XY]orus, depending on `flags')
+   *        0-(64+512*2) = 0-1088 bytes (depending on `n[XY]orus')
+   *        0-? (structure too complicated to be shown here; depending on
+   *             `flags')
+   *        1-? glyph data (faintly resembling PS Type 1 charstrings)
+   *
+   *         ==>   minimum size 3 bytes
+   *
+   *      compoundGps (size at most 2^16 bytes)
+   *        1 byte (nElements <= 63, flags)
+   *        + 0-(1+255*(2+255)) = 0-65536 (depending on `flags')
+   *        + nElements * (6-14 bytes)
+   *
+   *      bitmapGps (size at most 2^16 bytes)
+   *        1 byte (flags)
+   *        3-13 bytes (position info, depending on `flags')
+   *        0-? bitmap data
+   *
+   *         ==>   minimum size 4 bytes
+   *
+   *    PFR trailer
+   *        8 bytes
+   *
+   *
+   * ==>   minimum size of a valid PFR:
+   *         58 (header)
+   *         + 2 (nLogFonts)
+   *         + 27 (1 physFontRecord)
+   *         + 8 (trailer)
+   *        -----
+   *         95 bytes
+   *
+   */
+
+
   /*************************************************************************/
   /*************************************************************************/
   /*****                                                               *****/
@@ -212,6 +299,16 @@
          FT_READ_USHORT( count )          )
       goto Exit;
 
+    /* check maximum value and a rough minimum size */
+    if ( count > ( ( 1 << 16 ) - 2 ) / 5                ||
+         2 + count * 5 >= stream->size - section_offset )
+    {
+      FT_ERROR(( "pfr_log_font_count:"
+                 " invalid number of logical fonts\n" ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
     result = count;
 
   Exit:
@@ -530,8 +627,6 @@
     FT_Memory     memory = phy_font->memory;
 
 
-    FT_TRACE2(( "pfr_extra_item_load_kerning_pairs()\n" ));
-
     if ( FT_NEW( item ) )
       goto Exit;
 
@@ -782,7 +877,7 @@
       FT_Byte*  q2;
 
 
-      PFR_CHECK( num_aux );
+      PFR_CHECK_SIZE( num_aux );
       p += num_aux;
 
       while ( num_aux > 0 )
@@ -871,9 +966,6 @@
       phy_font->num_chars    = count = PFR_NEXT_USHORT( p );
       phy_font->chars_offset = offset + (FT_Offset)( p - stream->cursor );
 
-      if ( FT_NEW_ARRAY( phy_font->chars, count ) )
-        goto Fail;
-
       Size = 1 + 1 + 2;
       if ( flags & PFR_PHY_2BYTE_CHARCODE )
         Size += 1;
@@ -890,7 +982,10 @@
       if ( flags & PFR_PHY_3BYTE_GPS_OFFSET )
         Size += 1;
 
-      PFR_CHECK( count * Size );
+      PFR_CHECK_SIZE( count * Size );
+
+      if ( FT_NEW_ARRAY( phy_font->chars, count ) )
+        goto Fail;
 
       for ( n = 0; n < count; n++ )
       {
diff --git a/src/pfr/pfrload.h b/src/pfr/pfrload.h
index 0a51234..867e632 100644
--- a/src/pfr/pfrload.h
+++ b/src/pfr/pfrload.h
@@ -25,14 +25,19 @@
 
 FT_BEGIN_HEADER
 
+  /* some size checks should be always done (mainly to prevent */
+  /* excessive allocation for malformed data), ...             */
+#define PFR_CHECK_SIZE( x )  do                       \
+                             {                        \
+                               if ( p + (x) > limit ) \
+                                 goto Too_Short;      \
+                             } while ( 0 )
+
+  /* ... and some only if intensive checking is explicitly requested */
 #ifdef PFR_CONFIG_NO_CHECKS
 #define PFR_CHECK( x )  do { } while ( 0 )
 #else
-#define PFR_CHECK( x )  do                       \
-                        {                        \
-                          if ( p + (x) > limit ) \
-                            goto Too_Short;      \
-                        } while ( 0 )
+#define PFR_CHECK  PFR_CHECK_SIZE
 #endif
 
 #define PFR_NEXT_BYTE( p )    FT_NEXT_BYTE( p )
diff --git a/src/pfr/pfrsbit.c b/src/pfr/pfrsbit.c
index bed082e..a219636 100644
--- a/src/pfr/pfrsbit.c
+++ b/src/pfr/pfrsbit.c
@@ -510,8 +510,7 @@
         break;
 
       default:
-        FT_ERROR(( "pfr_read_bitmap_data: invalid image type\n" ));
-        error = FT_THROW( Invalid_File_Format );
+        ;
       }
     }
 
@@ -633,6 +632,53 @@
                                        &advance, &format );
 
       /*
+       * Before allocating the target bitmap, we check whether the given
+       * bitmap dimensions are valid, depending on the image format.
+       *
+       * Format 0: We have a stream of pixels (with 8 pixels per byte).
+       *
+       *             (xsize * ysize + 7) / 8 <= gps_size
+       *
+       * Format 1: Run-length encoding; the high nibble holds the number of
+       *           white bits, the low nibble the number of black bits.  In
+       *           other words, a single byte can represent at most 15
+       *           pixels.
+       *
+       *             xsize * ysize <= 15 * gps_size
+       *
+       * Format 2: Run-length encoding; the high byte holds the number of
+       *           white bits, the low byte the number of black bits.  In
+       *           other words, two bytes can represent at most 255 pixels.
+       *
+       *             xsize * ysize <= 255 * (gps_size + 1) / 2
+       */
+      switch ( format )
+      {
+      case 0:
+        if ( ( (FT_ULong)xsize * ysize + 7 ) / 8 > gps_size )
+          error = FT_THROW( Invalid_Table );
+        break;
+      case 1:
+        if ( (FT_ULong)xsize * ysize > 15 * gps_size )
+          error = FT_THROW( Invalid_Table );
+        break;
+      case 2:
+        if ( (FT_ULong)xsize * ysize > 255 * ( ( gps_size + 1 ) / 2 ) )
+          error = FT_THROW( Invalid_Table );
+        break;
+      default:
+        FT_ERROR(( "pfr_slot_load_bitmap: invalid image type\n" ));
+        error = FT_THROW( Invalid_Table );
+      }
+
+      if ( error )
+      {
+        if ( FT_ERR_EQ( error, Invalid_Table ) )
+          FT_ERROR(( "pfr_slot_load_bitmap: invalid bitmap dimensions\n" ));
+        goto Exit;
+      }
+
+      /*
        * XXX: on 16bit systems we return an error for huge bitmaps
        *      that cause size truncation, because truncated
        *      size properties make bitmap glyphs broken.