Commit d609b7c1582a9714bbad831fc5f955bccca436a4

Werner Lemberg 2016-09-09T22:11:07

[sfnt] Don't provide (completely) broken strike data. FreeType tries to sanitize strike header data; we now reject completely broken ones. * include/freetype/internal/tttypes.h (TT_FaceRec): New `sbit_strike_map' array pointer. * src/base/ftobjs.c (FT_Match_Size): Reject matches where either width or height would be zero. Add tracing message in case of error. * src/sfnt/sfobjs.c (sfnt_load_face): Populate `sbit_strike_map', only using (more or less) valid strike header data for FT_Face's `available_sizes' array. (sfnt_done_face): Updated. * src/sfnt/ttsbit.c (tt_face_set_sbit_strike): Use `sbit_strike_map'. (tt_face_load_strike_metrics): Improve tracing. * src/truetype/ttdriver.c (tt_size_select): Use `sbit_strike_map'.

diff --git a/ChangeLog b/ChangeLog
index c7321f6..a1bcfb0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2016-09-09  Werner Lemberg  <wl@gnu.org>
+
+	[sfnt] Don't provide (completely) broken strike data.
+
+	FreeType tries to sanitize strike header data; we now reject
+	completely broken ones.
+
+	* include/freetype/internal/tttypes.h (TT_FaceRec): New
+	`sbit_strike_map' array pointer.
+
+	* src/base/ftobjs.c (FT_Match_Size): Reject matches where either
+	width or height would be zero.
+	Add tracing message in case of error.
+
+	* src/sfnt/sfobjs.c (sfnt_load_face): Populate `sbit_strike_map',
+	only using (more or less) valid strike header data for
+	FT_Face's `available_sizes' array.
+	(sfnt_done_face): Updated.
+
+	* src/sfnt/ttsbit.c (tt_face_set_sbit_strike): Use
+	`sbit_strike_map'.
+	(tt_face_load_strike_metrics): Improve tracing.
+
+	* src/truetype/ttdriver.c (tt_size_select): Use `sbit_strike_map'.
+
 2016-09-08  Werner Lemberg  <wl@gnu.org>
 
 	* Version 2.7 released.
diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h
index 3a50734..10503fd 100644
--- a/include/freetype/freetype.h
+++ b/include/freetype/freetype.h
@@ -951,6 +951,10 @@ FT_BEGIN_HEADER
   /*                           strikes in the face.  It is set to NULL if  */
   /*                           there is no bitmap strike.                  */
   /*                                                                       */
+  /*                           Note that FreeType tries to sanitize the    */
+  /*                           strike data since they are sometimes sloppy */
+  /*                           or incorrect, but this can easily fail.     */
+  /*                                                                       */
   /*    num_charmaps        :: The number of charmaps in the face.         */
   /*                                                                       */
   /*    charmaps            :: An array of the charmaps of the face.       */
diff --git a/include/freetype/internal/tttypes.h b/include/freetype/internal/tttypes.h
index 4ed980b..1767132 100644
--- a/include/freetype/internal/tttypes.h
+++ b/include/freetype/internal/tttypes.h
@@ -1371,6 +1371,7 @@ FT_BEGIN_HEADER
     FT_ULong              sbit_table_size;
     TT_SbitTableType      sbit_table_type;
     FT_UInt               sbit_num_strikes;
+    FT_UInt*              sbit_strike_map;
 
     FT_Byte*              kern_table;
     FT_ULong              kern_table_size;
diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c
index 0c9e409..9006b59 100644
--- a/src/base/ftobjs.c
+++ b/src/base/ftobjs.c
@@ -2631,6 +2631,9 @@
     w = FT_PIX_ROUND( w );
     h = FT_PIX_ROUND( h );
 
+    if ( !w || !h )
+      return FT_THROW( Invalid_Pixel_Size );
+
     for ( i = 0; i < face->num_fixed_sizes; i++ )
     {
       FT_Bitmap_Size*  bsize = face->available_sizes + i;
@@ -2650,6 +2653,8 @@
       }
     }
 
+    FT_TRACE3(( "FT_Match_Size: no matching bitmap strike\n" ));
+
     return FT_THROW( Invalid_Pixel_Size );
   }
 
diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c
index 4413bbc..5d5a448 100644
--- a/src/sfnt/sfobjs.c
+++ b/src/sfnt/sfobjs.c
@@ -1411,7 +1411,7 @@
        *  depths in the FT_Bitmap_Size record.  This is a design error.
        */
       {
-        FT_UInt  i, count;
+        FT_UInt  count;
 
 
         count = face->sbit_num_strikes;
@@ -1423,6 +1423,8 @@
           FT_Short         avgwidth = face->os2.xAvgCharWidth;
           FT_Size_Metrics  metrics;
 
+          FT_UInt  strike_idx, bsize_idx;
+
 
           if ( em_size == 0 || face->os2.version == 0xFFFFU )
           {
@@ -1430,31 +1432,43 @@
             em_size = 1;
           }
 
-          if ( FT_NEW_ARRAY( root->available_sizes, count ) )
+          /* to avoid invalid strike data in the `available_sizes' field */
+          /* of `FT_Face', we map `available_sizes' indices to strike    */
+          /* indices                                                     */
+          if ( FT_NEW_ARRAY( root->available_sizes, count ) ||
+               FT_NEW_ARRAY( face->sbit_strike_map, count ) )
             goto Exit;
 
-          for ( i = 0; i < count; i++ )
+          bsize_idx = 0;
+          for ( strike_idx = 0; strike_idx < count; strike_idx++ )
           {
-            FT_Bitmap_Size*  bsize = root->available_sizes + i;
+            FT_Bitmap_Size*  bsize = root->available_sizes + bsize_idx;
 
 
-            error = sfnt->load_strike_metrics( face, i, &metrics );
+            error = sfnt->load_strike_metrics( face, strike_idx, &metrics );
             if ( error )
               goto Exit;
 
             bsize->height = (FT_Short)( metrics.height >> 6 );
-            bsize->width = (FT_Short)(
-                ( avgwidth * metrics.x_ppem + em_size / 2 ) / em_size );
+            bsize->width  = (FT_Short)(
+              ( avgwidth * metrics.x_ppem + em_size / 2 ) / em_size );
 
             bsize->x_ppem = metrics.x_ppem << 6;
             bsize->y_ppem = metrics.y_ppem << 6;
 
             /* assume 72dpi */
             bsize->size   = metrics.y_ppem << 6;
+
+            /* only use strikes with valid PPEM values */
+            if ( bsize->x_ppem && bsize->y_ppem )
+              face->sbit_strike_map[bsize_idx++] = strike_idx;
           }
 
+          /* reduce array size to the actually used elements */
+          (void)FT_RENEW_ARRAY( face->sbit_strike_map, count, bsize_idx );
+
           root->face_flags     |= FT_FACE_FLAG_FIXED_SIZES;
-          root->num_fixed_sizes = (FT_Int)count;
+          root->num_fixed_sizes = (FT_Int)bsize_idx;
         }
       }
 
@@ -1648,6 +1662,7 @@
 
     /* freeing sbit size table */
     FT_FREE( face->root.available_sizes );
+    FT_FREE( face->sbit_strike_map );
     face->root.num_fixed_sizes = 0;
 
     FT_FREE( face->postscript_name );
diff --git a/src/sfnt/ttsbit.c b/src/sfnt/ttsbit.c
index 36c261d..b4902be 100644
--- a/src/sfnt/ttsbit.c
+++ b/src/sfnt/ttsbit.c
@@ -251,7 +251,14 @@
                            FT_Size_Request  req,
                            FT_ULong*        astrike_index )
   {
-    return FT_Match_Size( (FT_Face)face, req, 0, astrike_index );
+    FT_Error  error;
+
+
+    error = FT_Match_Size( (FT_Face)face, req, 0, astrike_index );
+    if ( !error )
+      *astrike_index = face->sbit_strike_map[*astrike_index];
+
+    return error;
   }
 
 
@@ -305,7 +312,8 @@
             FT_TRACE2(( "tt_face_load_strike_metrics:"
                         " sanitizing invalid ascender and descender\n"
                         "                            "
-                        " values for strike (%d, %d)\n",
+                        " values for strike %d (%dppem, %dppem)\n",
+                        strike_index,
                         metrics->x_ppem, metrics->y_ppem ));
 
             /* sanitize buggy ascender and descender values */
diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c
index 6520c93..8c98867 100644
--- a/src/truetype/ttdriver.c
+++ b/src/truetype/ttdriver.c
@@ -273,25 +273,27 @@
 
   static FT_Error
   tt_size_select( FT_Size   size,
-                  FT_ULong  strike_index )
+                  FT_ULong  bsize_index )
   {
     TT_Face   ttface = (TT_Face)size->face;
     TT_Size   ttsize = (TT_Size)size;
     FT_Error  error  = FT_Err_Ok;
 
+    FT_UInt  strike_index = ttface->sbit_strike_map[bsize_index];
+
 
     ttsize->strike_index = strike_index;
 
     if ( FT_IS_SCALABLE( size->face ) )
     {
       /* use the scaled metrics, even when tt_size_reset fails */
-      FT_Select_Metrics( size->face, strike_index );
+      FT_Select_Metrics( size->face, bsize_index );
 
       tt_size_reset( ttsize ); /* ignore return value */
     }
     else
     {
-      SFNT_Service      sfnt    = (SFNT_Service) ttface->sfnt;
+      SFNT_Service      sfnt    = (SFNT_Service)ttface->sfnt;
       FT_Size_Metrics*  metrics = &size->metrics;
 
 
@@ -319,7 +321,7 @@
     if ( FT_HAS_FIXED_SIZES( size->face ) )
     {
       TT_Face       ttface = (TT_Face)size->face;
-      SFNT_Service  sfnt   = (SFNT_Service) ttface->sfnt;
+      SFNT_Service  sfnt   = (SFNT_Service)ttface->sfnt;
       FT_ULong      strike_index;