diff --git a/ChangeLog b/ChangeLog index 8c079d101..131e7c9e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2021-06-28 Dominik Röttsches + + [sfnt] Improve paint limit checks + + Paint tables can appear before the `base_glyphs_v1` offset if the + font is produced with the layer list before the base glyph list. In + this case paint tables can occur after the layer list but before the + base glyph list. Checks in the 'COLR' v1 code were rejecting fonts + with this layout. Improve these checks by calculating a minimum + offset after which paint tables can occur and use that in safety + checks. + + * src/sfnt/ttcolr.c (Colr, tt_face_load_colr): Declare + `paint_start_v1` and calculate that as the minimum of the end of + layer list and base glyph list. + (get_child_table_pointer, read_paint, tt_face_get_paint_layers): + Use that in safety checks. + 2021-06-28 Alexei Podtelezhnikov [raster] Clean up vertical sweep. @@ -202,7 +220,7 @@ 2021-06-09 Alexei Podtelezhnikov - * src/truetype/ttinterp.c (TT_RunIns): Optimize tracing. + * src/truetype/ttinterp.c (TT_RunIns): Optimize tracing. 2021-06-09 Alexei Podtelezhnikov diff --git a/src/sfnt/ttcolr.c b/src/sfnt/ttcolr.c index 8db774c0d..1e297ac8c 100644 --- a/src/sfnt/ttcolr.c +++ b/src/sfnt/ttcolr.c @@ -41,7 +41,7 @@ /* NOTE: These are the table sizes calculated through the specs. */ #define BASE_GLYPH_SIZE 6U -#define BASE_GLYPH_V1_RECORD_SIZE 6U +#define BASE_GLYPH_PAINT_RECORD_SIZE 6U #define LAYER_V1_LIST_PAINT_OFFSET_SIZE 4U #define LAYER_V1_LIST_NUM_LAYERS_SIZE 4U #define COLOR_STOP_SIZE 6U @@ -83,6 +83,13 @@ FT_ULong num_layers_v1; FT_Byte* layers_v1; + /* + * Paint tables start at the minimum of the end of the LayerList and the + * end of the BaseGlyphList. Record this location in a field here for + * safety checks when accessing paint tables. + */ + FT_Byte* paints_start_v1; + /* The memory that backs up the `COLR' table. */ void* table; FT_ULong table_size; @@ -170,7 +177,7 @@ p1 = (FT_Byte*)( table + base_glyphs_offset_v1 ); num_base_glyphs_v1 = FT_PEEK_ULONG( p1 ); - if ( num_base_glyphs_v1 * BASE_GLYPH_V1_RECORD_SIZE > + if ( num_base_glyphs_v1 * BASE_GLYPH_PAINT_RECORD_SIZE > table_size - base_glyphs_offset_v1 ) goto InvalidTable; @@ -185,8 +192,18 @@ p1 = (FT_Byte*)( table + layer_offset_v1 ); num_layers_v1 = FT_PEEK_ULONG( p1 ); + if ( num_layers_v1 * LAYER_V1_LIST_PAINT_OFFSET_SIZE > + table_size - layer_offset_v1 ) + goto InvalidTable; + colr->num_layers_v1 = num_layers_v1; colr->layers_v1 = p1; + + colr->paints_start_v1 = + FT_MIN( colr->base_glyphs_v1 + + colr->num_base_glyphs_v1 * BASE_GLYPH_PAINT_RECORD_SIZE, + colr->layers_v1 + + colr->num_layers_v1 * LAYER_V1_LIST_PAINT_OFFSET_SIZE ); } colr->base_glyphs = (FT_Byte*)( table + base_glyph_offset ); @@ -367,7 +384,7 @@ child_table_p = (FT_Byte*)( paint_base + paint_offset ); - if ( child_table_p < colr->base_glyphs_v1 || + if ( child_table_p < colr->paints_start_v1 || child_table_p >= ( (FT_Byte*)colr->table + colr->table_size ) ) return 0; @@ -388,7 +405,7 @@ if ( !p || !colr || !colr->table ) return 0; - if ( p < colr->base_glyphs_v1 || + if ( p < colr->paints_start_v1 || p >= ( (FT_Byte*)colr->table + colr->table_size ) ) return 0; @@ -608,7 +625,7 @@ * skip `numBaseGlyphV1Records` by adding 4 to start binary search * in the array of `BaseGlyphV1Record`. */ - FT_Byte *p = base_glyph_begin + 4 + mid * BASE_GLYPH_V1_RECORD_SIZE; + FT_Byte *p = base_glyph_begin + 4 + mid * BASE_GLYPH_PAINT_RECORD_SIZE; FT_UShort gid = FT_NEXT_USHORT( p ); @@ -704,7 +721,7 @@ /* * First ensure that p is within COLRv1. */ - if ( p < colr->base_glyphs_v1 || + if ( p < colr->layers_v1 || p >= ( (FT_Byte*)colr->table + colr->table_size ) ) return 0; @@ -731,7 +748,7 @@ p_paint = (FT_Byte*)( colr->layers_v1 + paint_offset ); - if ( p_paint < colr->base_glyphs_v1 || + if ( p_paint < colr->paints_start_v1 || p_paint >= ( (FT_Byte*)colr->table + colr->table_size ) ) return 0;