[truetype] Fix handling of packed deltas in Variation Fonts.

* src/truetype/ttgxvar (ft_var_readpackeddeltas): Don't expect the number of
bytes used to encode the deltas to be higher than the number of encoded
values.  The specification allows a very compact encoding; for example, a
list of 200 zeros can be encoded with just a couple of bytes.

We now count the consumed bytes to make sure to not read more than expected.
This commit is contained in:
Jany Belluz 2021-11-04 11:07:43 +00:00 committed by Werner Lemberg
parent 9ed5332fe6
commit fc55291b1c
1 changed files with 39 additions and 14 deletions

View File

@ -264,55 +264,80 @@
FT_Fixed *deltas = NULL; FT_Fixed *deltas = NULL;
FT_UInt runcnt, cnt; FT_UInt runcnt, cnt;
FT_UInt i, j; FT_UInt i, j;
FT_UInt bytes_used;
FT_Memory memory = stream->memory; FT_Memory memory = stream->memory;
FT_Error error = FT_Err_Ok; FT_Error error = FT_Err_Ok;
FT_UNUSED( error ); FT_UNUSED( error );
if ( delta_cnt > size )
{
FT_TRACE1(( "ft_var_readpackeddeltas: number of points too large\n" ));
return NULL;
}
if ( FT_NEW_ARRAY( deltas, delta_cnt ) ) if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
return NULL; return NULL;
i = 0; i = 0;
while ( i < delta_cnt ) bytes_used = 0;
while ( i < delta_cnt && bytes_used < size )
{ {
runcnt = FT_GET_BYTE(); runcnt = FT_GET_BYTE();
cnt = runcnt & GX_DT_DELTA_RUN_COUNT_MASK; cnt = runcnt & GX_DT_DELTA_RUN_COUNT_MASK;
bytes_used++;
if ( runcnt & GX_DT_DELTAS_ARE_ZERO ) if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
{ {
/* `runcnt' zeroes get added */ /* `cnt` + 1 zeroes get added */
for ( j = 0; j <= cnt && i < delta_cnt; j++ ) for ( j = 0; j <= cnt && i < delta_cnt; j++ )
deltas[i++] = 0; deltas[i++] = 0;
} }
else if ( runcnt & GX_DT_DELTAS_ARE_WORDS ) else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
{ {
/* `runcnt' shorts from the stack */ /* `cnt` + 1 shorts from the stack */
bytes_used += 2 * ( cnt + 1 );
if ( bytes_used > size )
{
FT_TRACE1(( "ft_var_readpackeddeltas:"
" number of short deltas too large\n" ));
goto Fail;
}
for ( j = 0; j <= cnt && i < delta_cnt; j++ ) for ( j = 0; j <= cnt && i < delta_cnt; j++ )
deltas[i++] = FT_intToFixed( FT_GET_SHORT() ); deltas[i++] = FT_intToFixed( FT_GET_SHORT() );
} }
else else
{ {
/* `runcnt' signed bytes from the stack */ /* `cnt` + 1 signed bytes from the stack */
bytes_used += cnt + 1;
if ( bytes_used > size )
{
FT_TRACE1(( "ft_var_readpackeddeltas:"
" number of byte deltas too large\n" ));
goto Fail;
}
for ( j = 0; j <= cnt && i < delta_cnt; j++ ) for ( j = 0; j <= cnt && i < delta_cnt; j++ )
deltas[i++] = FT_intToFixed( FT_GET_CHAR() ); deltas[i++] = FT_intToFixed( FT_GET_CHAR() );
} }
if ( j <= cnt ) if ( j <= cnt )
{ {
/* bad format */ FT_TRACE1(( "ft_var_readpackeddeltas:"
FT_FREE( deltas ); " number of deltas too large\n" ));
return NULL; goto Fail;
} }
} }
if ( i < delta_cnt )
{
FT_TRACE1(( "ft_var_readpackeddeltas: not enough deltas\n" ));
goto Fail;
}
return deltas; return deltas;
Fail:
FT_FREE( deltas );
return NULL;
} }