[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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
diff --git a/ChangeLog b/ChangeLog
index 04b2d54..a1d3e37 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
diff --git a/docs/CHANGES b/docs/CHANGES
index 49590f8..307d83c 100644
--- a/docs/CHANGES
+++ b/docs/CHANGES
@@ -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
diff --git a/src/truetype/ttinterp.c b/src/truetype/ttinterp.c
index 2d15ea7..a5244eb 100644
--- a/src/truetype/ttinterp.c
+++ b/src/truetype/ttinterp.c
@@ -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 )
diff --git a/src/truetype/ttinterp.h b/src/truetype/ttinterp.h
index 53f0944..be87e08 100644
--- a/src/truetype/ttinterp.h
+++ b/src/truetype/ttinterp.h
@@ -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;