diff --git a/ChangeLog b/ChangeLog index e6a4528d3..eaaa21e45 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,28 @@ +2019-10-08 Werner Lemberg + + [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 - * 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 5741fbabb..7b1689d81 100644 --- a/include/freetype/internal/wofftypes.h +++ b/include/freetype/internal/wofftypes.h @@ -176,38 +176,6 @@ FT_BEGIN_HEADER } WOFF2_HeaderRec, *WOFF2_Header; - /************************************************************************** - * - * @struct: - * WOFF2_InfoRec - * - * @description: - * Metadata for WOFF2 font that may be required for reconstruction of - * sfnt tables. - * - * @fields: - * header_checksum :: - * Checksum of SFNT offset table. - * - * num_glyphs :: - * Number of glyphs in the font. - * - * num_hmetrics :: - * `numberOfHMetrics` field in the 'hhea' table. - * - * x_mins :: - * `xMin` values of glyph bounding box. - */ - typedef struct WOFF2_InfoRec_ - { - FT_ULong header_checksum; - FT_UShort num_glyphs; - FT_UShort num_hmetrics; - FT_Short* x_mins; - - } WOFF2_InfoRec, *WOFF2_Info; - - /************************************************************************** * * @struct: @@ -236,6 +204,51 @@ FT_BEGIN_HEADER } WOFF2_TableRec, *WOFF2_Table; + /************************************************************************** + * + * @struct: + * WOFF2_InfoRec + * + * @description: + * Metadata for WOFF2 font that may be required for reconstruction of + * sfnt tables. + * + * @fields: + * header_checksum :: + * Checksum of SFNT offset table. + * + * num_glyphs :: + * Number of glyphs in the font. + * + * num_hmetrics :: + * `numberOfHMetrics` field in the 'hhea' table. + * + * 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_ + { + FT_ULong header_checksum; + FT_UShort num_glyphs; + FT_UShort num_hmetrics; + FT_Short* x_mins; + + WOFF2_Table glyf_table; + WOFF2_Table loca_table; + WOFF2_Table head_table; + + } WOFF2_InfoRec, *WOFF2_Info; + + /************************************************************************** * * @struct: diff --git a/src/sfnt/sfwoff2.c b/src/sfnt/sfwoff2.c index 246842516..01d5bb241 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;