Commit 5a1a79c0e8de8e886cc347ad22801982e8298a06

Werner Lemberg 2019-10-08T11:12:18

[woff2] Fix SFNT table checks. Also reduce number of SFNT table lookups. Reported as https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=18065 * include/freetype/internal/wofftypes.h (WOFF2_InfoRec): Add fields `glyf_table', `loca_table', and `head_table'. * src/sfnt/sfwoff2.c (reconstruct_glyf): Update signature. Use table pointers in `info' parameter. (get_x_mins): Check `maxp_table' Use table pointers in `info' parameter. (reconstruct_font): Use and set table pointers in `info' parameter. Fix check for `glyf' and `loca' tables. Update call to `reconstruct_glyf'. (woff2_open_font): Updated.

diff --git a/ChangeLog b/ChangeLog
index e6a4528..eaaa21e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,28 @@
+2019-10-08  Werner Lemberg  <wl@gnu.org>
+
+	[woff2] Fix SFNT table checks.
+
+	Also reduce number of SFNT table lookups.
+
+	Reported as
+
+	  https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=18065
+
+	* include/freetype/internal/wofftypes.h (WOFF2_InfoRec): Add fields
+	`glyf_table', `loca_table', and `head_table'.
+
+	* src/sfnt/sfwoff2.c (reconstruct_glyf): Update signature.
+	Use table pointers in `info' parameter.
+	(get_x_mins): Check `maxp_table'
+	Use table pointers in `info' parameter.
+	(reconstruct_font):  Use and set table pointers in `info' parameter.
+	Fix check for `glyf' and `loca' tables.
+	Update call to `reconstruct_glyf'.
+	(woff2_open_font): Updated.
+
 2019-10-06  Werner Lemberg  <wl@gnu.org>
 
-	* src/sfnt/sfwoff2 (reconstruct_glyf): Fix reallocation.
+	* src/sfnt/sfwoff2.c (reconstruct_glyf): Fix reallocation.
 
 	Reported as
 
diff --git a/include/freetype/internal/wofftypes.h b/include/freetype/internal/wofftypes.h
index 5741fba..7b1689d 100644
--- a/include/freetype/internal/wofftypes.h
+++ b/include/freetype/internal/wofftypes.h
@@ -179,6 +179,34 @@ FT_BEGIN_HEADER
   /**************************************************************************
    *
    * @struct:
+   *   WOFF2_TableRec
+   *
+   * @description:
+   *   This structure describes a given table of a WOFF2 font.
+   *
+   * @fields:
+   *   See
+   *
+   *     https://www.w3.org/TR/WOFF2/#table_dir_format
+   */
+  typedef struct  WOFF2_TableRec_
+  {
+    FT_Byte   FlagByte;           /* table type and flags      */
+    FT_ULong  Tag;                /* table file offset         */
+    FT_ULong  dst_length;         /* uncompressed table length */
+    FT_ULong  TransformLength;    /* transformed length        */
+
+    FT_ULong  flags;              /* calculated flags          */
+    FT_ULong  src_offset;         /* compressed table offset   */
+    FT_ULong  src_length;         /* compressed table length   */
+    FT_ULong  dst_offset;         /* uncompressed table offset */
+
+  } WOFF2_TableRec, *WOFF2_Table;
+
+
+  /**************************************************************************
+   *
+   * @struct:
    *   WOFF2_InfoRec
    *
    * @description:
@@ -197,6 +225,15 @@ FT_BEGIN_HEADER
    *
    *   x_mins ::
    *     `xMin` values of glyph bounding box.
+   *
+   *   glyf_table ::
+   *     A pointer to the `glyf' table record.
+   *
+   *   loca_table ::
+   *     A pointer to the `loca' table record.
+   *
+   *   head_table ::
+   *     A pointer to the `head' table record.
    */
   typedef struct  WOFF2_InfoRec_
   {
@@ -205,35 +242,11 @@ FT_BEGIN_HEADER
     FT_UShort  num_hmetrics;
     FT_Short*  x_mins;
 
-  } WOFF2_InfoRec, *WOFF2_Info;
-
-
-  /**************************************************************************
-   *
-   * @struct:
-   *   WOFF2_TableRec
-   *
-   * @description:
-   *   This structure describes a given table of a WOFF2 font.
-   *
-   * @fields:
-   *   See
-   *
-   *     https://www.w3.org/TR/WOFF2/#table_dir_format
-   */
-  typedef struct  WOFF2_TableRec_
-  {
-    FT_Byte   FlagByte;           /* table type and flags      */
-    FT_ULong  Tag;                /* table file offset         */
-    FT_ULong  dst_length;         /* uncompressed table length */
-    FT_ULong  TransformLength;    /* transformed length        */
-
-    FT_ULong  flags;              /* calculated flags          */
-    FT_ULong  src_offset;         /* compressed table offset   */
-    FT_ULong  src_length;         /* compressed table length   */
-    FT_ULong  dst_offset;         /* uncompressed table offset */
+    WOFF2_Table  glyf_table;
+    WOFF2_Table  loca_table;
+    WOFF2_Table  head_table;
 
-  } WOFF2_TableRec, *WOFF2_Table;
+  } WOFF2_InfoRec, *WOFF2_Info;
 
 
   /**************************************************************************
diff --git a/src/sfnt/sfwoff2.c b/src/sfnt/sfwoff2.c
index 2468425..01d5bb2 100644
--- a/src/sfnt/sfwoff2.c
+++ b/src/sfnt/sfwoff2.c
@@ -827,9 +827,7 @@
 
   static FT_Error
   reconstruct_glyf( FT_Stream    stream,
-                    WOFF2_Table  glyf_table,
                     FT_ULong*    glyf_checksum,
-                    WOFF2_Table  loca_table,
                     FT_ULong*    loca_checksum,
                     FT_Byte**    sfnt_bytes,
                     FT_ULong*    sfnt_size,
@@ -887,11 +885,11 @@
     /* index_format = 1 => Long version `loca'.                */
     expected_loca_length = ( index_format ? 4 : 2 ) *
                              ( (FT_ULong)num_glyphs + 1 );
-    if ( loca_table->dst_length != expected_loca_length )
+    if ( info->loca_table->dst_length != expected_loca_length )
       goto Fail;
 
     offset = ( 2 + num_substreams ) * 4;
-    if ( offset > glyf_table->TransformLength )
+    if ( offset > info->glyf_table->TransformLength )
       goto Fail;
 
     for ( i = 0; i < num_substreams; ++i )
@@ -901,7 +899,7 @@
 
       if ( FT_READ_ULONG( substream_size ) )
         goto Fail;
-      if ( substream_size > glyf_table->TransformLength - offset )
+      if ( substream_size > info->glyf_table->TransformLength - offset )
         goto Fail;
 
       substreams[i].start  = pos + offset;
@@ -1196,11 +1194,11 @@
         info->x_mins[i] = x_min;
     }
 
-    glyf_table->dst_length = dest_offset - glyf_table->dst_offset;
-    loca_table->dst_offset = dest_offset;
+    info->glyf_table->dst_length = dest_offset - info->glyf_table->dst_offset;
+    info->loca_table->dst_offset = dest_offset;
 
     /* `loca[n]' will be equal to the length of the `glyf' table. */
-    loca_values[num_glyphs] = glyf_table->dst_length;
+    loca_values[num_glyphs] = info->glyf_table->dst_length;
 
     if ( store_loca( loca_values,
                      num_glyphs + 1,
@@ -1212,11 +1210,11 @@
                      memory ) )
       goto Fail;
 
-    loca_table->dst_length = dest_offset - loca_table->dst_offset;
+    info->loca_table->dst_length = dest_offset - info->loca_table->dst_offset;
 
     FT_TRACE4(( "  loca table info:\n" ));
-    FT_TRACE4(( "    dst_offset = %lu\n", loca_table->dst_offset ));
-    FT_TRACE4(( "    dst_length = %lu\n", loca_table->dst_length ));
+    FT_TRACE4(( "    dst_offset = %lu\n", info->loca_table->dst_offset ));
+    FT_TRACE4(( "    dst_length = %lu\n", info->loca_table->dst_length ));
     FT_TRACE4(( "    checksum = %09x\n", *loca_checksum ));
 
     /* Set pointer `sfnt_bytes' to its correct value. */
@@ -1265,16 +1263,16 @@
     FT_Error   error = FT_Err_Ok;
     FT_ULong   offset_size;
 
-    const WOFF2_Table glyf_table = find_table( tables, num_tables,
-                                               TTAG_glyf );
-    const WOFF2_Table loca_table = find_table( tables, num_tables,
-                                               TTAG_loca );
-    const WOFF2_Table maxp_table = find_table( tables, num_tables,
-                                               TTAG_maxp );
-    const WOFF2_Table head_table = find_table( tables, num_tables,
-                                               TTAG_head );
+    const WOFF2_Table  maxp_table = find_table( tables, num_tables,
+                                                TTAG_maxp );
 
 
+    if ( !maxp_table )
+    {
+      FT_ERROR(( "`maxp' table is missing.\n" ));
+      return FT_THROW( Invalid_Table );
+    }
+
     /* Read `numGlyphs' field from `maxp' table. */
     if ( FT_STREAM_SEEK( maxp_table->src_offset ) && FT_STREAM_SKIP( 8 ) )
       return error;
@@ -1285,7 +1283,8 @@
     info->num_glyphs = num_glyphs;
 
     /* Read `indexToLocFormat' field from `head' table. */
-    if ( FT_STREAM_SEEK( head_table->src_offset ) && FT_STREAM_SKIP( 50 ) )
+    if ( FT_STREAM_SEEK( info->head_table->src_offset ) &&
+         FT_STREAM_SKIP( 50 )                           )
       return error;
 
     if ( FT_READ_USHORT( index_format ) )
@@ -1297,7 +1296,7 @@
     if ( FT_NEW_ARRAY( info->x_mins, num_glyphs ) )
       return error;
 
-    loca_offset = loca_table->src_offset;
+    loca_offset = info->loca_table->src_offset;
 
     for ( i = 0; i < num_glyphs; ++i )
     {
@@ -1320,7 +1319,7 @@
         glyf_offset = glyf_offset << 1;
       }
 
-      glyf_offset += glyf_table->src_offset;
+      glyf_offset += info->glyf_table->src_offset;
 
       if ( FT_STREAM_SEEK( glyf_offset ) && FT_STREAM_SKIP( 2 ) )
         return error;
@@ -1501,29 +1500,25 @@
     FT_ULong   font_checksum = info->header_checksum;
     FT_Bool    is_glyf_xform = FALSE;
 
-    FT_ULong     table_entry_offset = 12;
-    WOFF2_Table  head_table;
+    FT_ULong  table_entry_offset = 12;
+
 
     /* A few table checks before reconstruction. */
     /* `glyf' must be present with `loca'.       */
-    const WOFF2_Table glyf_table = find_table( indices, num_tables,
-                                               TTAG_glyf );
-    const WOFF2_Table loca_table = find_table( indices, num_tables,
-                                               TTAG_loca );
-
+    info->glyf_table = find_table( indices, num_tables, TTAG_glyf );
+    info->loca_table = find_table( indices, num_tables, TTAG_loca );
 
-    if ( ( !glyf_table && loca_table ) ||
-         ( !loca_table && glyf_table ) )
+    if ( !( info->glyf_table && info->loca_table ) )
     {
       FT_ERROR(( "Both `glyph' and `loca' tables must be present.\n" ));
       return FT_THROW( Invalid_Table );
     }
 
     /* Both `glyf' and `loca' must have same transformation. */
-    if ( glyf_table != NULL )
+    if ( info->glyf_table != NULL )
     {
-      if ( ( glyf_table->flags & WOFF2_FLAGS_TRANSFORM ) !=
-           ( loca_table->flags & WOFF2_FLAGS_TRANSFORM ) )
+      if ( ( info->glyf_table->flags & WOFF2_FLAGS_TRANSFORM ) !=
+           ( info->loca_table->flags & WOFF2_FLAGS_TRANSFORM ) )
       {
         FT_ERROR(( "Transformation mismatch"
                    " between `glyf' and `loca' table." ));
@@ -1605,9 +1600,7 @@
           table.dst_offset = dest_offset;
 
           if ( reconstruct_glyf( stream,
-                                 &table,
                                  &checksum,
-                                 loca_table,
                                  &loca_checksum,
                                  &sfnt,
                                  sfnt_size,
@@ -1677,17 +1670,17 @@
     }
 
     /* Update `head' checkSumAdjustment. */
-    head_table = find_table( indices, num_tables, TTAG_head );
-    if ( !head_table )
+    info->head_table = find_table( indices, num_tables, TTAG_head );
+    if ( !info->head_table )
     {
       FT_ERROR(( "`head' table is missing.\n" ));
       goto Fail;
     }
 
-    if ( head_table->dst_length < 12 )
+    if ( info->head_table->dst_length < 12 )
       goto Fail;
 
-    buf_cursor    = sfnt + head_table->dst_offset + 8;
+    buf_cursor    = sfnt + info->head_table->dst_offset + 8;
     font_checksum = 0xB1B0AFBA - font_checksum;
 
     WRITE_ULONG( buf_cursor, font_checksum );
@@ -1734,7 +1727,7 @@
     FT_Int     face_index;
 
     WOFF2_HeaderRec  woff2;
-    WOFF2_InfoRec    info         = { 0, 0, 0, NULL };
+    WOFF2_InfoRec    info         = { 0, 0, 0, NULL, NULL, NULL, NULL };
     WOFF2_Table      tables       = NULL;
     WOFF2_Table*     indices      = NULL;
     WOFF2_Table*     temp_indices = NULL;