[truetype] Introduce dynamic limits for some bytecode opcodes.

This speeds up FreeType's handling of malformed fonts.

* src/truetype/ttinterp.c (TT_RunIns): Set up limits for the number
of twilight points, the total number of negative jumps, and the
total number of loops in LOOPCALL opcodes.  The values are based on
the number of points and entries in the CVT table.
(Ins_JMPR): Test negative jump counter.
(Ins_LOOPCALL): Test loopcall counter.

* src/truetype/ttinterp.h (TT_ExecContext): Updated.

* docs/CHANGES: Updated.
This commit is contained in:
Werner Lemberg 2016-09-27 08:44:31 +02:00
parent a764963f26
commit 0d94592942
4 changed files with 106 additions and 0 deletions

View File

@ -1,3 +1,20 @@
2016-09-27 Werner Lemberg <wl@gnu.org>
[truetype] Introduce dynamic limits for some bytecode opcodes.
This speeds up FreeType's handling of malformed fonts.
* src/truetype/ttinterp.c (TT_RunIns): Set up limits for the number
of twilight points, the total number of negative jumps, and the
total number of loops in LOOPCALL opcodes. The values are based on
the number of points and entries in the CVT table.
(Ins_JMPR): Test negative jump counter.
(Ins_LOOPCALL): Test loopcall counter.
* src/truetype/ttinterp.h (TT_ExecContext): Updated.
* docs/CHANGES: Updated.
2016-09-25 Werner Lemberg <wl@gnu.org>
[truetype] Sanitize only last entry of `loca' table.

View File

@ -1,4 +1,31 @@
CHANGES BETWEEN 2.7 and 2.7.1
I. IMPORTANT CHANGES
II. IMPORTANT BUG FIXES
III. MISCELLANEOUS
- Some limits for TrueType bytecode execution have been tightened
to speed up FreeType's handling of malformed fonts, in
particular to quickly abort endless loops.
- The number of twilight points can no longer be set to an
arbitrarily large value.
- The total number of jump opcode instructions (like JMPR) with
negative arguments is dynamically restricted; the same holds
for the total number of iterations in LOOPCALL opcodes.
The dynamic limits are based on the number of points in a glyph
and the number of CVT entries. Please report if you encounter a
font where the selected values are not adequate.
======================================================================
CHANGES BETWEEN 2.6.5 and 2.7
I. IMPORTANT CHANGES

View File

@ -3388,13 +3388,27 @@
FT_Long* args )
{
if ( args[0] == 0 && exc->args == 0 )
{
exc->error = FT_THROW( Bad_Argument );
return;
}
exc->IP += args[0];
if ( exc->IP < 0 ||
( exc->callTop > 0 &&
exc->IP > exc->callStack[exc->callTop - 1].Def->end ) )
{
exc->error = FT_THROW( Bad_Argument );
return;
}
exc->step_ins = FALSE;
if ( args[0] < 0 )
{
if ( ++exc->neg_jump_counter > exc->neg_jump_counter_max )
exc->error = FT_THROW( Execution_Too_Long );
}
}
@ -3949,6 +3963,10 @@
Ins_Goto_CodeRange( exc, def->range, def->start );
exc->step_ins = FALSE;
exc->loopcall_counter += args[0];
if ( exc->loopcall_counter > exc->loopcall_counter_max )
exc->error = FT_THROW( Execution_Too_Long );
}
return;
@ -7533,6 +7551,7 @@
TT_RunIns( TT_ExecContext exc )
{
FT_Long ins_counter = 0; /* executed instructions counter */
FT_Long num_twilight_points;
FT_UShort i;
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
@ -7568,6 +7587,41 @@
exc->iupy_called = FALSE;
#endif
/* We restrict the number of twilight points to a reasonable, */
/* heuristic value to avoid slow execution of malformed bytecode. */
num_twilight_points = FT_MAX( 30,
2 * ( exc->pts.n_points + exc->cvtSize ) );
if ( exc->twilight.n_points > num_twilight_points )
{
FT_TRACE5(( "TT_RunIns: Resetting number of twilight points\n"
" from %d to the more reasonable value %d\n",
exc->twilight.n_points,
num_twilight_points ));
exc->twilight.n_points = num_twilight_points;
}
/* Set up loop detectors. We restrict the number of LOOPCALL loops */
/* and the number of JMPR, JROT, and JROF calls with a negative */
/* argument to values that depend on the size of the CVT table and */
/* the number of points in the current glyph (if applicable). */
/* */
/* The idea is that in real-world bytecode you either iterate over */
/* all CVT entries, or over all points (or contours) of a glyph, and */
/* such iterations don't happen very often. */
exc->loopcall_counter = 0;
exc->neg_jump_counter = 0;
/* The maximum values are heuristic. */
exc->loopcall_counter_max = FT_MAX( 100,
10 * ( exc->pts.n_points +
exc->cvtSize ) );
FT_TRACE5(( "TT_RunIns: Limiting total number of loops in LOOPCALL"
" to %d\n", exc->loopcall_counter_max ));
exc->neg_jump_counter_max = exc->loopcall_counter_max;
FT_TRACE5(( "TT_RunIns: Limiting total number of backward jumps"
" to %d\n", exc->neg_jump_counter_max ));
/* set PPEM and CVT functions */
exc->tt_metrics.ratio = 0;
if ( exc->metrics.x_ppem != exc->metrics.y_ppem )

View File

@ -408,6 +408,14 @@ FT_BEGIN_HEADER
#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */
/* We maintain two counters (in addition to the instruction counter) */
/* that act as loop detectors for LOOPCALL and jump opcodes with */
/* negative arguments. */
FT_Long loopcall_counter;
FT_Long loopcall_counter_max;
FT_Long neg_jump_counter;
FT_Long neg_jump_counter_max;
} TT_ExecContextRec;