Commit 66630d882cd6e0653acf4f86b65b6b6070caff88

Alexei Podtelezhnikov 2021-05-03T22:40:16

[sfnt] Streamline POST format 2.0 handing. To reduce memory allocations, we read an entire Pascal-string buffer and convert it to a C-string buffer. We also reject tables with Postscript glyph names exceeding 63 bytes. * src/sfnt/ttpost.c (load_format20): Implement it. (load_post_names): Check the minimal POST table size. (load_format25, tt_face_free_ps_names): Updated accordingly.

diff --git a/ChangeLog b/ChangeLog
index 122fc56..998155a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,16 @@
-2021-04-25  Alexei Podtelezhnikov  <apodtele@gmail.com>
+2021-05-03  Alexei Podtelezhnikov  <apodtele@gmail.com>
+
+	[sfnt] Streamline POST format 2.0 handing.
+
+	To reduce memory allocations, we read an entire Pascal-string buffer
+	and convert it to a C-string buffer.  We also reject tables with
+	Postscript glyph names exceeding 63 bytes.
+
+	* src/sfnt/ttpost.c (load_format20): Implement it.
+	(load_post_names): Check the minimal POST table size.
+	(load_format25, tt_face_free_ps_names): Updated accordingly.
+
+2021-05-02  Alexei Podtelezhnikov  <apodtele@gmail.com>
 
 	[bdf,pcf] Avoid memory zeroing (contd.).
 
diff --git a/src/sfnt/ttpost.c b/src/sfnt/ttpost.c
index 6d34ce4..5ce7706 100644
--- a/src/sfnt/ttpost.c
+++ b/src/sfnt/ttpost.c
@@ -158,7 +158,7 @@
   static FT_Error
   load_format_20( TT_Face    face,
                   FT_Stream  stream,
-                  FT_ULong   post_limit )
+                  FT_ULong   post_len )
   {
     FT_Memory   memory = stream->memory;
     FT_Error    error;
@@ -168,6 +168,7 @@
 
     FT_UShort*  glyph_indices = NULL;
     FT_Char**   name_strings  = NULL;
+    FT_Byte*    strings       = NULL;
 
 
     if ( FT_READ_USHORT( num_glyphs ) )
@@ -179,7 +180,8 @@
     /* There already exist fonts which have more than 32768 glyph names */
     /* in this table, so the test for this threshold has been dropped.  */
 
-    if ( num_glyphs > face->max_profile.numGlyphs )
+    if ( num_glyphs > face->max_profile.numGlyphs ||
+         num_glyphs * 2UL > post_len - 2          )
     {
       error = FT_THROW( Invalid_File_Format );
       goto Exit;
@@ -190,8 +192,8 @@
       FT_Int  n;
 
 
-      if ( FT_QNEW_ARRAY ( glyph_indices, num_glyphs ) ||
-           FT_FRAME_ENTER( num_glyphs * 2L )           )
+      if ( FT_QNEW_ARRAY( glyph_indices, num_glyphs ) ||
+           FT_FRAME_ENTER( num_glyphs * 2L )          )
         goto Fail;
 
       for ( n = 0; n < num_glyphs; n++ )
@@ -223,60 +225,56 @@
     }
 
     /* now load the name strings */
+    if ( num_names )
     {
       FT_UShort  n;
+      FT_ULong   p;
+
 
+      post_len -= num_glyphs * 2UL + 2;
 
-      if ( FT_QNEW_ARRAY( name_strings, num_names ) )
+      if ( FT_QALLOC( strings, post_len + 1 )       ||
+           FT_STREAM_READ( strings, post_len )      ||
+           FT_QNEW_ARRAY( name_strings, num_names ) )
         goto Fail;
 
-      for ( n = 0; n < num_names; n++ )
+      /* convert from Pascal- to C-strings and set pointers */
+      for ( p = 0, n = 0; p < post_len && n < num_names; n++ )
       {
-        FT_UInt  len;
+        FT_UInt  len = strings[p];
 
 
-        if ( FT_STREAM_POS() >= post_limit )
-          break;
-        else
-        {
-          FT_TRACE6(( "load_format_20: %ld byte left in post table\n",
-                      post_limit - FT_STREAM_POS() ));
-
-          if ( FT_READ_BYTE( len ) )
-            goto Fail1;
-        }
-
-        if ( len > post_limit                   ||
-             FT_STREAM_POS() > post_limit - len )
+        if ( len > 63U )
         {
-          FT_Int  d = (FT_Int)post_limit - (FT_Int)FT_STREAM_POS();
-
-
-          FT_ERROR(( "load_format_20:"
-                     " exceeding string length (%d),"
-                     " truncating at end of post table (%d byte left)\n",
-                     len, d ));
-          len = (FT_UInt)FT_MAX( 0, d );
+          error = FT_THROW( Invalid_File_Format );
+          goto Fail;
         }
 
-        if ( FT_QNEW_ARRAY( name_strings[n], len + 1 ) ||
-             FT_STREAM_READ( name_strings[n], len    ) )
-          goto Fail1;
-
-        name_strings[n][len] = '\0';
+        strings[p]      = 0;
+        name_strings[n] = (FT_Char*)strings + p + 1;
+        p              += len + 1;
       }
+      strings[post_len] = 0;
 
+      /* deal with missing or insufficient string data */
       if ( n < num_names )
       {
+        if ( post_len == 0 )
+        {
+          /* fake empty string */
+          if ( FT_QREALLOC( strings, 1, 2 ) )
+            goto Fail;
+
+          post_len = 1;
+          strings[post_len] = 0;
+        }
+
         FT_ERROR(( "load_format_20:"
                    " all entries in post table are already parsed,"
                    " using NULL names for gid %d - %d\n",
                     n, num_names - 1 ));
         for ( ; n < num_names; n++ )
-          if ( FT_QNEW_ARRAY( name_strings[n], 1 ) )
-            goto Fail1;
-          else
-            name_strings[n][0] = '\0';
+          name_strings[n] = (FT_Char*)strings + post_len;
       }
     }
 
@@ -292,17 +290,9 @@
     }
     return FT_Err_Ok;
 
-  Fail1:
-    {
-      FT_UShort  n;
-
-
-      for ( n = 0; n < num_names; n++ )
-        FT_FREE( name_strings[n] );
-    }
-
   Fail:
     FT_FREE( name_strings );
+    FT_FREE( strings );
     FT_FREE( glyph_indices );
 
   Exit:
@@ -313,7 +303,7 @@
   static FT_Error
   load_format_25( TT_Face    face,
                   FT_Stream  stream,
-                  FT_ULong   post_limit )
+                  FT_ULong   post_len )
   {
     FT_Memory  memory = stream->memory;
     FT_Error   error;
@@ -321,7 +311,7 @@
     FT_Int     num_glyphs;
     FT_Char*   offset_table = NULL;
 
-    FT_UNUSED( post_limit );
+    FT_UNUSED( post_len );
 
 
     if ( FT_READ_USHORT( num_glyphs ) )
@@ -384,7 +374,6 @@
     FT_Error   error;
     FT_Fixed   format;
     FT_ULong   post_len;
-    FT_ULong   post_limit;
 
 
     /* get a stream for the face's resource */
@@ -395,8 +384,6 @@
     if ( error )
       goto Exit;
 
-    post_limit = FT_STREAM_POS() + post_len;
-
     format = face->postscript.FormatType;
 
     /* go to beginning of subtable */
@@ -404,10 +391,10 @@
       goto Exit;
 
     /* now read postscript table */
-    if ( format == 0x00020000L )
-      error = load_format_20( face, stream, post_limit );
-    else if ( format == 0x00025000L )
-      error = load_format_25( face, stream, post_limit );
+    if ( format == 0x00020000L && post_len >= 34 )
+      error = load_format_20( face, stream, post_len - 32 );
+    else if ( format == 0x00025000L && post_len >= 34 )
+      error = load_format_25( face, stream, post_len - 32 );
     else
       error = FT_THROW( Invalid_File_Format );
 
@@ -433,17 +420,19 @@
       if ( format == 0x00020000L )
       {
         TT_Post_20  table = &names->names.format_20;
-        FT_UShort   n;
 
 
         FT_FREE( table->glyph_indices );
         table->num_glyphs = 0;
 
-        for ( n = 0; n < table->num_names; n++ )
-          FT_FREE( table->glyph_names[n] );
+        if ( table->num_names )
+        {
+          table->glyph_names[0]--;
+          FT_FREE( table->glyph_names[0] );
 
-        FT_FREE( table->glyph_names );
-        table->num_names = 0;
+          FT_FREE( table->glyph_names );
+          table->num_names = 0;
+        }
       }
       else if ( format == 0x00025000L )
       {