Commit e6593389cf557740d6b15d6065bb96a4a81cabeb

Werner Lemberg 2015-10-21T08:04:29

[sfnt] Avoid unnecessarily large allocation for WOFFs (#46257). * src/sfnt/sfobjs.c (woff_open_font): Use WOFF's `totalSfntSize' only after thorough checks. Add tracing messages.

diff --git a/ChangeLog b/ChangeLog
index 0d52a85..cac6c75 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2015-10-21  Werner Lemberg  <wl@gnu.org>
 
+	[sfnt] Avoid unnecessarily large allocation for WOFFs (#46257).
+
+	* src/sfnt/sfobjs.c (woff_open_font): Use WOFF's `totalSfntSize'
+	only after thorough checks.
+	Add tracing messages.
+
+2015-10-21  Werner Lemberg  <wl@gnu.org>
+
 	[type42] Better check invalid `sfnts' array data (#46255).
 
 	* src/type42/t42parse.c (t42_parse_sfnts): Table lengths must be
diff --git a/src/autofit/afranges.c b/src/autofit/afranges.c
index 13c2213..64edc79 100644
--- a/src/autofit/afranges.c
+++ b/src/autofit/afranges.c
@@ -179,6 +179,34 @@
   };
 
 
+  const AF_Script_UniRangeRec  af_khmr_uniranges[] =
+  {
+    AF_UNIRANGE_REC(  0x1780UL,  0x17FFUL ),  /* Khmer */
+    AF_UNIRANGE_REC(       0UL,       0UL )
+  };
+
+  const AF_Script_UniRangeRec  af_khmr_nonbase_uniranges[] =
+  {
+    AF_UNIRANGE_REC(  0x17B7UL,  0x17BDUL ),
+    AF_UNIRANGE_REC(  0x17C6UL,  0x17C6UL ),
+    AF_UNIRANGE_REC(  0x17C9UL,  0x17D3UL ),
+    AF_UNIRANGE_REC(  0x17DDUL,  0x17DDUL ),
+    AF_UNIRANGE_REC(       0UL,       0UL )
+  };
+
+
+  const AF_Script_UniRangeRec  af_khms_uniranges[] =
+  {
+    AF_UNIRANGE_REC(  0x19E0UL,  0x19FFUL ),  /* Khmer Symbols */
+    AF_UNIRANGE_REC(       0UL,       0UL )
+  };
+
+  const AF_Script_UniRangeRec  af_khms_nonbase_uniranges[] =
+  {
+    AF_UNIRANGE_REC( 0UL, 0UL )
+  };
+
+
   const AF_Script_UniRangeRec  af_lao_uniranges[] =
   {
     AF_UNIRANGE_REC(  0x0E80UL,  0x0EFFUL ),  /* Lao */
diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c
index 42c7222..eabe865 100644
--- a/src/sfnt/sfobjs.c
+++ b/src/sfnt/sfobjs.c
@@ -451,10 +451,14 @@
                                      woff.metaOrigLength != 0 ) ) ||
          ( woff.metaLength != 0 && woff.metaOrigLength == 0 )     ||
          ( woff.privOffset == 0 && woff.privLength != 0 )         )
+    {
+      FT_ERROR(( "woff_font_open: invalid WOFF header\n" ));
       return FT_THROW( Invalid_Table );
+    }
 
-    if ( FT_ALLOC( sfnt, woff.totalSfntSize ) ||
-         FT_NEW( sfnt_stream )                )
+    /* Don't trust `totalSfntSize' before thorough checks. */
+    if ( FT_ALLOC( sfnt, 12 + woff.num_tables * 16UL ) ||
+         FT_NEW( sfnt_stream )                         )
       goto Exit;
 
     sfnt_header = sfnt;
@@ -521,6 +525,8 @@
       if ( table->Tag <= old_tag )
       {
         FT_FRAME_EXIT();
+
+        FT_ERROR(( "woff_font_open: table tags are not sorted\n" ));
         error = FT_THROW( Invalid_Table );
         goto Exit;
       }
@@ -555,6 +561,7 @@
            sfnt_offset > woff.totalSfntSize - table->OrigLength ||
            table->CompLength > table->OrigLength                )
       {
+        FT_ERROR(( "woff_font_open: invalid table offsets\n" ));
         error = FT_THROW( Invalid_Table );
         goto Exit;
       }
@@ -580,6 +587,8 @@
       if ( woff.metaOffset != woff_offset                  ||
            woff.metaOffset + woff.metaLength > woff.length )
       {
+        FT_ERROR(( "woff_font_open:"
+                   " invalid `metadata' offset or length\n" ));
         error = FT_THROW( Invalid_Table );
         goto Exit;
       }
@@ -596,6 +605,7 @@
       if ( woff.privOffset != woff_offset                  ||
            woff.privOffset + woff.privLength > woff.length )
       {
+        FT_ERROR(( "woff_font_open: invalid `private' offset or length\n" ));
         error = FT_THROW( Invalid_Table );
         goto Exit;
       }
@@ -607,10 +617,19 @@
     if ( sfnt_offset != woff.totalSfntSize ||
          woff_offset != woff.length        )
     {
+      FT_ERROR(( "woff_font_open: invalid `sfnt' table structure\n" ));
       error = FT_THROW( Invalid_Table );
       goto Exit;
     }
 
+    /* Now use `totalSfntSize'. */
+    if ( FT_REALLOC( sfnt,
+                     12 + woff.num_tables * 16UL,
+                     woff.totalSfntSize ) )
+      goto Exit;
+
+    sfnt_header = sfnt + 12;
+
     /* Write the tables. */
 
     for ( nn = 0; nn < woff.num_tables; nn++ )
@@ -651,6 +670,7 @@
           goto Exit;
         if ( output_len != table->OrigLength )
         {
+          FT_ERROR(( "woff_font_open: compressed table length mismatch\n" ));
           error = FT_THROW( Invalid_Table );
           goto Exit;
         }
diff --git a/src/tools/ftfuzzer/ftfuzzer.cc b/src/tools/ftfuzzer/ftfuzzer.cc
index e5ab293..45d0137 100644
--- a/src/tools/ftfuzzer/ftfuzzer.cc
+++ b/src/tools/ftfuzzer/ftfuzzer.cc
@@ -3,6 +3,9 @@
 #  error "a C++11 compiler is needed"
 #endif
 
+#include <archive.h>
+#include <archive_entry.h>
+
 #include <assert.h>
 #include <stdint.h>
 
@@ -45,6 +48,59 @@ using namespace std;
 
   FT_Global  global_ft;
 
+  static int
+  archive_read_entry_data( struct archive *ar, vector<FT_Byte> *vw )
+  {
+    int r;
+    const FT_Byte *buff;
+    size_t size;
+    int64_t offset;
+
+    for (;;) {
+      r = archive_read_data_block( ar, reinterpret_cast<const void**>(&buff), &size, &offset );
+      if (r == ARCHIVE_EOF)
+        return (ARCHIVE_OK);
+      if (r != ARCHIVE_OK)
+        return (r);
+      vw->insert(vw->end(), buff, buff + size);
+    }
+ }
+
+  static vector<vector<FT_Byte>>
+  parse_data( const uint8_t*  data,
+              size_t          size )
+  {
+    struct archive_entry *entry;
+    int r;
+    vector<vector<FT_Byte>> files;
+
+    unique_ptr<struct archive, decltype ( archive_read_free )*> a( archive_read_new(), archive_read_free );
+    archive_read_support_format_tar(a.get());
+
+    // The need for the const_cast was removed with libarchive be4d4ddcfca77f6e43753156eaa919f4d25ed903
+    if (!(r = archive_read_open_memory( a.get(), const_cast<void*>(static_cast<const void*>(data)), size )))
+    {
+      unique_ptr<struct archive, decltype ( archive_read_close )*> a_open( a.get(), archive_read_close );
+      for (;;) {
+        r = archive_read_next_header( a_open.get(), &entry );
+        if (r == ARCHIVE_EOF)
+          break;
+        if (r != ARCHIVE_OK)
+          break;
+        vector<FT_Byte> entry_data;
+        r = archive_read_entry_data( a.get(), &entry_data );
+        if (r != ARCHIVE_OK)
+          break;
+        files.push_back( move( entry_data ) );
+      }
+    }
+
+    if (files.size() == 0)
+      files.emplace_back(data, data + size);
+
+    return files;
+  }
+
 
   static void
   setIntermediateAxis( FT_Face  face )
@@ -85,6 +141,8 @@ using namespace std;
 
     long  size = (long)size_;
 
+    const vector<vector<FT_Byte>>& files = parse_data( data, size );
+
     FT_Face         face;
     FT_Int32        load_flags  = FT_LOAD_DEFAULT;
 #if 0
@@ -99,7 +157,7 @@ using namespace std;
     // more than a single font.
 
     // get number of faces
-    if ( FT_New_Memory_Face( library, data, size, -1, &face ) )
+    if ( FT_New_Memory_Face( library, files[0].data(), files[0].size(), -1, &face ) )
       return 0;
     long  num_faces = face->num_faces;
     FT_Done_Face( face );
@@ -111,8 +169,8 @@ using namespace std;
     {
       // get number of instances
       if ( FT_New_Memory_Face( library,
-                               data,
-                               size,
+                               files[0].data(),
+                               files[0].size(),
                                -( face_index + 1 ),
                                &face ) )
         continue;
@@ -125,12 +183,23 @@ using namespace std;
             instance_index++ )
       {
         if ( FT_New_Memory_Face( library,
-                                 data,
-                                 size,
+                                 files[0].data(),
+                                 files[0].size(),
                                  ( instance_index << 16 ) + face_index,
                                  &face ) )
           continue;
 
+        for ( long files_index = 1;
+              files_index < files.size();
+              files_index++)
+        {
+          FT_Open_Args open_args = {};
+          open_args.flags = FT_OPEN_MEMORY;
+          open_args.memory_base = files[files_index].data();
+          open_args.memory_size = files[files_index].size();
+          FT_Attach_Stream( face, &open_args );
+        }
+
         // loop over all bitmap stroke sizes
         // and an arbitrary size for outlines
         for ( long  fixed_sizes_index = 0;