Skip to content

Commit 6a50160

Browse files
committed
Prevent RunLengthFilter malicious OOM
1 parent 73ce5bb commit 6a50160

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

src/UglyToad.PdfPig.Tests/Integration/GithubIssuesTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@
77

88
public class GithubIssuesTests
99
{
10+
[Fact]
11+
public void Issue1067()
12+
{
13+
var path = IntegrationHelpers.GetSpecificTestDocumentPath("GHOSTSCRIPT-691770-0.pdf");
14+
15+
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }))
16+
{
17+
var ex = Assert.Throws<PdfDocumentFormatException>(() => document.GetPage(1));
18+
Assert.StartsWith("Decoded stream size exceeds the estimated maximum size.", ex.Message);
19+
}
20+
}
21+
1022
[Fact]
1123
public void Issue1054()
1224
{
Binary file not shown.

src/UglyToad.PdfPig/PdfExtensions.cs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,21 @@ public static Memory<byte> Decode(this StreamToken stream, IFilterProvider filte
5959
{
6060
var filters = filterProvider.GetFilters(stream.StreamDictionary);
6161

62+
double totalMaxEstSize = stream.Data.Length * 100;
63+
6264
var transform = stream.Data;
6365
for (var i = 0; i < filters.Count; i++)
6466
{
65-
transform = filters[i].Decode(transform, stream.StreamDictionary, filterProvider, i);
67+
var filter = filters[i];
68+
totalMaxEstSize *= GetEstimatedSizeMultiplier(filter);
69+
70+
transform = filter.Decode(transform, stream.StreamDictionary, filterProvider, i);
71+
72+
if (i < filters.Count - 1 && transform.Length > totalMaxEstSize)
73+
{
74+
// Try to prevent malicious decompression, leading to OOM issues
75+
throw new PdfDocumentFormatException($"Decoded stream size exceeds the estimated maximum size. Current decoded stream length: {transform.Length}, {i + 1} filters applied out of {filters.Count}.");
76+
}
6677
}
6778

6879
return transform;
@@ -75,15 +86,38 @@ public static Memory<byte> Decode(this StreamToken stream, ILookupFilterProvider
7586
{
7687
var filters = filterProvider.GetFilters(stream.StreamDictionary, scanner);
7788

89+
double totalMaxEstSize = stream.Data.Length * 100;
90+
7891
var transform = stream.Data;
7992
for (var i = 0; i < filters.Count; i++)
8093
{
81-
transform = filters[i].Decode(transform, stream.StreamDictionary, filterProvider, i);
94+
var filter = filters[i];
95+
totalMaxEstSize *= GetEstimatedSizeMultiplier(filter);
96+
97+
transform = filter.Decode(transform, stream.StreamDictionary, filterProvider, i);
98+
99+
if (i < filters.Count - 1 && transform.Length > totalMaxEstSize)
100+
{
101+
// Try to prevent malicious decompression, leading to OOM issues
102+
throw new PdfDocumentFormatException($"Decoded stream size exceeds the estimated maximum size. Current decoded stream length: {transform.Length}, {i + 1} filters applied out of {filters.Count}.");
103+
}
82104
}
83105

84106
return transform;
85107
}
86108

109+
private static double GetEstimatedSizeMultiplier(IFilter filter)
110+
{
111+
return filter switch
112+
{
113+
AsciiHexDecodeFilter => 0.5,
114+
Ascii85Filter => 0.8,
115+
FlateFilter or RunLengthFilter => 3,
116+
LzwFilter => 2,
117+
_ => 1000
118+
};
119+
}
120+
87121
/// <summary>
88122
/// Returns an equivalent token where any indirect references of child objects are
89123
/// recursively traversed and resolved.

0 commit comments

Comments
 (0)