Skip to content

Commit 1b80769

Browse files
Merge pull request #280 from SixLabors/js/billion-lols
Prevent crash from Billion Laughs attack.
2 parents 034a440 + 7448d62 commit 1b80769

32 files changed

+172
-83
lines changed

src/SixLabors.Fonts/GlyphMetrics.cs

-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,6 @@ internal FontRectangle GetBoundingBox(Vector2 origin, float scaledPointSize)
190190

191191
internal void RenderDecorationsTo(IGlyphRenderer renderer, Vector2 location, float scaledPPEM)
192192
{
193-
// TODO: Move to base class
194193
(Vector2 Start, Vector2 End, float Thickness) GetEnds(float thickness, float position)
195194
{
196195
Vector2 scale = new Vector2(scaledPPEM) / this.ScaleFactor * MirrorScale;

src/SixLabors.Fonts/GlyphPositioningCollection.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ public bool TryAdd(Font font, GlyphSubstitutionCollection collection)
300300
/// </summary>
301301
/// <param name="fontMetrics">The font metrics.</param>
302302
/// <param name="index">The zero-based index of the element.</param>
303-
public void UpdatePosition(FontMetrics fontMetrics, ushort index)
303+
public void UpdatePosition(FontMetrics fontMetrics, int index)
304304
{
305305
GlyphShapingData data = this.GetGlyphShapingData(index);
306306
bool isDirtyXY = data.Bounds.IsDirtyXY;
@@ -338,7 +338,7 @@ public void UpdatePosition(FontMetrics fontMetrics, ushort index)
338338
/// <param name="glyphId">The id of the glyph to offset.</param>
339339
/// <param name="dx">The delta x-advance.</param>
340340
/// <param name="dy">The delta y-advance.</param>
341-
public void Advance(FontMetrics fontMetrics, ushort index, ushort glyphId, short dx, short dy)
341+
public void Advance(FontMetrics fontMetrics, int index, ushort glyphId, short dx, short dy)
342342
{
343343
foreach (GlyphMetrics m in this.glyphs[index].Metrics)
344344
{
@@ -355,7 +355,7 @@ public void Advance(FontMetrics fontMetrics, ushort index, ushort glyphId, short
355355
/// <param name="fontMetrics">The font face with metrics.</param>
356356
/// <param name="index">The zero-based index of the elements to position.</param>
357357
/// <returns><see langword="true"/> if the element should be processed; otherwise, <see langword="false"/>.</returns>
358-
public bool ShouldProcess(FontMetrics fontMetrics, ushort index)
358+
public bool ShouldProcess(FontMetrics fontMetrics, int index)
359359
=> this.glyphs[index].Metrics[0].FontMetrics == fontMetrics;
360360

361361
private class GlyphPositioningData

src/SixLabors.Fonts/GlyphSubstitutionCollection.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,12 @@ public void Replace(int index, ReadOnlySpan<ushort> glyphIds)
281281
glyphIds = glyphIds.Slice(1);
282282
for (int i = 0; i < glyphIds.Length; i++)
283283
{
284-
GlyphShapingData data = new(current, false);
285-
data.GlyphId = glyphIds[i];
286-
data.LigatureComponent = i + 1;
284+
GlyphShapingData data = new(current, false)
285+
{
286+
GlyphId = glyphIds[i],
287+
LigatureComponent = i + 1
288+
};
289+
287290
this.glyphs.Insert(++index, new(pair.Offset, data));
288291
}
289292
}

src/SixLabors.Fonts/StreamFontMetrics.cs

+11-3
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ internal override void UpdatePositions(GlyphPositioningCollection collection)
255255

256256
bool kerned = false;
257257
KerningMode kerningMode = collection.TextOptions.KerningMode;
258+
258259
gpos?.TryUpdatePositions(this, collection, out kerned);
259260

260261
if (!kerned && kerningMode != KerningMode.None)
@@ -263,11 +264,18 @@ internal override void UpdatePositions(GlyphPositioningCollection collection)
263264
? this.trueTypeFontTables!.Kern
264265
: this.compactFontTables!.Kern;
265266

266-
if (kern != null)
267+
if (kern?.Count > 0)
267268
{
268-
for (ushort index = 1; index < collection.Count; index++)
269+
// Set max constraints to prevent OutOfMemoryException or infinite loops from attacks.
270+
int maxCount = AdvancedTypographicUtils.GetMaxAllowableShapingCollectionCount(collection.Count);
271+
for (int index = 1; index < collection.Count; index++)
269272
{
270-
kern.UpdatePositions(this, collection, (ushort)(index - 1), index);
273+
if (index >= maxCount)
274+
{
275+
break;
276+
}
277+
278+
kern.UpdatePositions(this, collection, index - 1, index);
271279
}
272280
}
273281
}

src/SixLabors.Fonts/Tables/AdvancedTypographic/AdvancedTypographicUtils.cs

+21-10
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,21 @@ namespace SixLabors.Fonts.Tables.AdvancedTypographic
1010
{
1111
internal static class AdvancedTypographicUtils
1212
{
13-
/// <summary>
14-
/// The maximum length of a context. Taken from HarfBuzz - hb-ot-layout-common.hh
15-
/// </summary>
13+
// The following properties are used to prevent overflows caused
14+
// by maliciously crafted fonts.
15+
// Based on HarfBuzz hb-buffer.hh
1616
public const int MaxContextLength = 64;
17+
private const int MaxLengthFactor = 64;
18+
private const int MaxLengthMinimum = 16384;
19+
private const int MaxOperationsFactor = 1024;
20+
private const int MaxOperationsMinimum = 16384;
21+
private const int MaxShapingCharsLength = 0x3FFFFFFF; // Half int max.
22+
23+
public static int GetMaxAllowableShapingCollectionCount(int length)
24+
=> (int)Math.Min(Math.Max((long)length * MaxLengthFactor, MaxLengthMinimum), MaxShapingCharsLength);
25+
26+
public static int GetMaxAllowableShapingOperationsCount(int length)
27+
=> (int)Math.Min(Math.Max((long)length * MaxOperationsFactor, MaxOperationsMinimum), MaxShapingCharsLength);
1728

1829
public static bool ApplyLookupList(
1930
FontMetrics fontMetrics,
@@ -22,7 +33,7 @@ public static bool ApplyLookupList(
2233
LookupFlags lookupFlags,
2334
SequenceLookupRecord[] records,
2435
GlyphSubstitutionCollection collection,
25-
ushort index,
36+
int index,
2637
int count)
2738
{
2839
bool hasChanged = false;
@@ -56,7 +67,7 @@ public static bool ApplyLookupList(
5667
LookupFlags lookupFlags,
5768
SequenceLookupRecord[] records,
5869
GlyphPositioningCollection collection,
59-
ushort index,
70+
int index,
6071
int count)
6172
{
6273
bool hasChanged = false;
@@ -189,7 +200,7 @@ public static bool CheckAllCoverages(
189200
FontMetrics fontMetrics,
190201
LookupFlags lookupFlags,
191202
IGlyphShapingCollection collection,
192-
ushort index,
203+
int index,
193204
int count,
194205
CoverageTable[] input,
195206
CoverageTable[] backtrack,
@@ -224,7 +235,7 @@ public static bool CheckAllCoverages(
224235
public static void ApplyAnchor(
225236
FontMetrics fontMetrics,
226237
GlyphPositioningCollection collection,
227-
ushort index,
238+
int index,
228239
AnchorTable baseAnchor,
229240
MarkRecord markRecord,
230241
int baseGlyphIndex)
@@ -242,7 +253,7 @@ public static void ApplyAnchor(
242253

243254
public static void ApplyPosition(
244255
GlyphPositioningCollection collection,
245-
ushort index,
256+
int index,
246257
ValueRecord record)
247258
{
248259
GlyphShapingData current = collection.GetGlyphShapingData(index);
@@ -302,8 +313,8 @@ private static bool Match<T>(
302313
Func<T, GlyphShapingData, bool> condition,
303314
Span<int> matches)
304315
{
305-
ushort position = iterator.Index;
306-
ushort offset = iterator.Increment(increment);
316+
int position = iterator.Index;
317+
int offset = iterator.Increment(increment);
307318
IGlyphShapingCollection collection = iterator.Collection;
308319

309320
int i = 0;

src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupListTable.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public bool TryUpdatePosition(
144144
GPosTable table,
145145
GlyphPositioningCollection collection,
146146
Tag feature,
147-
ushort index,
147+
int index,
148148
int count)
149149
{
150150
foreach (LookupSubTable subTable in this.LookupSubTables)
@@ -172,7 +172,7 @@ public abstract bool TryUpdatePosition(
172172
GPosTable table,
173173
GlyphPositioningCollection collection,
174174
Tag feature,
175-
ushort index,
175+
int index,
176176
int count);
177177
}
178178
}

src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType1SubTable.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public override bool TryUpdatePosition(
7070
GPosTable table,
7171
GlyphPositioningCollection collection,
7272
Tag feature,
73-
ushort index,
73+
int index,
7474
int count)
7575
{
7676
ushort glyphId = collection[index];
@@ -141,7 +141,7 @@ public override bool TryUpdatePosition(
141141
GPosTable table,
142142
GlyphPositioningCollection collection,
143143
Tag feature,
144-
ushort index,
144+
int index,
145145
int count)
146146
{
147147
ushort glyphId = collection[index];

src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType2SubTable.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public override bool TryUpdatePosition(
9292
GPosTable table,
9393
GlyphPositioningCollection collection,
9494
Tag feature,
95-
ushort index,
95+
int index,
9696
int count)
9797
{
9898
if (count <= 1)
@@ -122,7 +122,7 @@ public override bool TryUpdatePosition(
122122
AdvancedTypographicUtils.ApplyPosition(collection, index, record1);
123123

124124
ValueRecord record2 = pairValueRecord.ValueRecord2;
125-
AdvancedTypographicUtils.ApplyPosition(collection, (ushort)(index + 1), record2);
125+
AdvancedTypographicUtils.ApplyPosition(collection, index + 1, record2);
126126

127127
return true;
128128
}
@@ -257,7 +257,7 @@ public override bool TryUpdatePosition(
257257
GPosTable table,
258258
GlyphPositioningCollection collection,
259259
Tag feature,
260-
ushort index,
260+
int index,
261261
int count)
262262
{
263263
if (count <= 1)
@@ -290,7 +290,7 @@ public override bool TryUpdatePosition(
290290
AdvancedTypographicUtils.ApplyPosition(collection, index, record1);
291291

292292
ValueRecord record2 = class2Record.ValueRecord2;
293-
AdvancedTypographicUtils.ApplyPosition(collection, (ushort)(index + 1), record2);
293+
AdvancedTypographicUtils.ApplyPosition(collection, index + 1, record2);
294294

295295
return true;
296296
}

src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType3SubTable.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public override bool TryUpdatePosition(
7777
GPosTable table,
7878
GlyphPositioningCollection collection,
7979
Tag feature,
80-
ushort index,
80+
int index,
8181
int count)
8282
{
8383
if (count <= 1)
@@ -93,7 +93,7 @@ public override bool TryUpdatePosition(
9393
return false;
9494
}
9595

96-
ushort nextIndex = (ushort)(index + 1);
96+
int nextIndex = index + 1;
9797
ushort nextGlyphId = collection[nextIndex];
9898
if (nextGlyphId == 0)
9999
{

src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType4SubTable.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public override bool TryUpdatePosition(
8686
GPosTable table,
8787
GlyphPositioningCollection collection,
8888
Tag feature,
89-
ushort index,
89+
int index,
9090
int count)
9191
{
9292
// Mark-to-Base Attachment Positioning Subtable.

src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType5SubTable.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public override bool TryUpdatePosition(
9090
GPosTable table,
9191
GlyphPositioningCollection collection,
9292
Tag feature,
93-
ushort index,
93+
int index,
9494
int count)
9595
{
9696
// Mark-to-Ligature Attachment Positioning.

src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType6SubTable.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public override bool TryUpdatePosition(
8888
GPosTable table,
8989
GlyphPositioningCollection collection,
9090
Tag feature,
91-
ushort index,
91+
int index,
9292
int count)
9393
{
9494
// Mark to mark positioning.

src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType7SubTable.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public override bool TryUpdatePosition(
5151
GPosTable table,
5252
GlyphPositioningCollection collection,
5353
Tag feature,
54-
ushort index,
54+
int index,
5555
int count)
5656
{
5757
ushort glyphId = collection[index];
@@ -130,7 +130,7 @@ public override bool TryUpdatePosition(
130130
GPosTable table,
131131
GlyphPositioningCollection collection,
132132
Tag feature,
133-
ushort index,
133+
int index,
134134
int count)
135135
{
136136
ushort glyphId = collection[index];
@@ -206,7 +206,7 @@ public override bool TryUpdatePosition(
206206
GPosTable table,
207207
GlyphPositioningCollection collection,
208208
Tag feature,
209-
ushort index,
209+
int index,
210210
int count)
211211
{
212212
ushort glyphId = collection[index];

src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/LookupType8SubTable.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public override bool TryUpdatePosition(
5454
GPosTable table,
5555
GlyphPositioningCollection collection,
5656
Tag feature,
57-
ushort index,
57+
int index,
5858
int count)
5959
{
6060
// Implements Chained Contexts Substitution, Format 1:
@@ -100,7 +100,7 @@ public override bool TryUpdatePosition(
100100
SequenceLookupRecord sequenceLookupRecord = rule.SequenceLookupRecords[j];
101101
LookupTable lookup = table.LookupList.LookupTables[sequenceLookupRecord.LookupListIndex];
102102
ushort sequenceIndex = sequenceLookupRecord.SequenceIndex;
103-
if (lookup.TryUpdatePosition(fontMetrics, table, collection, feature, (ushort)(index + sequenceIndex), 1))
103+
if (lookup.TryUpdatePosition(fontMetrics, table, collection, feature, index + sequenceIndex, 1))
104104
{
105105
hasChanged = true;
106106
}
@@ -155,7 +155,7 @@ public override bool TryUpdatePosition(
155155
GPosTable table,
156156
GlyphPositioningCollection collection,
157157
Tag feature,
158-
ushort index,
158+
int index,
159159
int count)
160160
{
161161
// Implements Chained Contexts Substitution for Format 2:
@@ -198,7 +198,7 @@ public override bool TryUpdatePosition(
198198
SequenceLookupRecord sequenceLookupRecord = rule.SequenceLookupRecords[j];
199199
LookupTable lookup = table.LookupList.LookupTables[sequenceLookupRecord.LookupListIndex];
200200
ushort sequenceIndex = sequenceLookupRecord.SequenceIndex;
201-
if (lookup.TryUpdatePosition(fontMetrics, table, collection, feature, (ushort)(index + sequenceIndex), 1))
201+
if (lookup.TryUpdatePosition(fontMetrics, table, collection, feature, index + sequenceIndex, 1))
202202
{
203203
hasChanged = true;
204204
}
@@ -249,7 +249,7 @@ public override bool TryUpdatePosition(
249249
GPosTable table,
250250
GlyphPositioningCollection collection,
251251
Tag feature,
252-
ushort index,
252+
int index,
253253
int count)
254254
{
255255
ushort glyphId = collection[index];
@@ -271,7 +271,7 @@ public override bool TryUpdatePosition(
271271
ushort lookupIndex = lookupRecord.LookupListIndex;
272272

273273
LookupTable lookup = table.LookupList.LookupTables[lookupIndex];
274-
if (lookup.TryUpdatePosition(fontMetrics, table, collection, feature, (ushort)(index + sequenceIndex), count - sequenceIndex))
274+
if (lookup.TryUpdatePosition(fontMetrics, table, collection, feature, index + sequenceIndex, count - sequenceIndex))
275275
{
276276
hasChanged = true;
277277
}

src/SixLabors.Fonts/Tables/AdvancedTypographic/GPos/NotImplementedSubTable.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public override bool TryUpdatePosition(
1515
GPosTable table,
1616
GlyphPositioningCollection collection,
1717
Tag feature,
18-
ushort index,
18+
int index,
1919
int count)
2020
=> false;
2121
}

0 commit comments

Comments
 (0)