From e6593389cf557740d6b15d6065bb96a4a81cabeb Mon Sep 17 00:00:00 2001 From: Werner Lemberg Date: Wed, 21 Oct 2015 08:04:29 +0200 Subject: [PATCH] [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. --- ChangeLog | 8 ++++ src/autofit/afranges.c | 28 ++++++++++++ src/sfnt/sfobjs.c | 24 ++++++++++- src/tools/ftfuzzer/ftfuzzer.cc | 79 +++++++++++++++++++++++++++++++--- 4 files changed, 132 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0d52a85ab..cac6c7500 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2015-10-21 Werner Lemberg + + [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 [type42] Better check invalid `sfnts' array data (#46255). diff --git a/src/autofit/afranges.c b/src/autofit/afranges.c index 13c221364..64edc79cb 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 42c7222f6..eabe865aa 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 e5ab293f1..45d0137ec 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 +#include + #include #include @@ -45,6 +48,59 @@ using namespace std; FT_Global global_ft; + static int + archive_read_entry_data( struct archive *ar, vector *vw ) + { + int r; + const FT_Byte *buff; + size_t size; + int64_t offset; + + for (;;) { + r = archive_read_data_block( ar, reinterpret_cast(&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> + parse_data( const uint8_t* data, + size_t size ) + { + struct archive_entry *entry; + int r; + vector> files; + + unique_ptr 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(static_cast(data)), size ))) + { + unique_ptr 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 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>& 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;