Prevent RunLengthFilter malicious OOM

This commit is contained in:
BobLd
2025-06-29 11:17:07 +01:00
parent 73ce5bbb73
commit 6a50160e65
3 changed files with 123 additions and 2 deletions

View File

@@ -7,6 +7,18 @@
public class GithubIssuesTests
{
[Fact]
public void Issue1067()
{
var path = IntegrationHelpers.GetSpecificTestDocumentPath("GHOSTSCRIPT-691770-0.pdf");
using (var document = PdfDocument.Open(path, new ParsingOptions() { UseLenientParsing = true }))
{
var ex = Assert.Throws<PdfDocumentFormatException>(() => document.GetPage(1));
Assert.StartsWith("Decoded stream size exceeds the estimated maximum size.", ex.Message);
}
}
[Fact]
public void Issue1054()
{

View File

@@ -0,0 +1,75 @@
%PDF-1.1
1 0 obj
<<
/Type /Catalog
/Outlines 2 0 R
/Pages 3 0 R
>>
endobj
2 0 obj
<<
/Type /Outlines
/Count 0
>>
endobj
3 0 obj
<<
/Type /Pages
/Kids [4 0 R]
/Count 1
>>
endobj
4 0 obj
<<
/Type /Page
/Parent 3 0 R
/MediaBox [0 0 612 792]
/Contents 5 0 R
/Resources <<
/ProcSet [/PDF /Text]
/Font << /F1 6 0 R >>
>>
>>
endobj
5 0 obj
<<
/Length 5
/Filter [/ASCIIHexDecode /RunLengthDecode /RunLengthDecode /RunLengthDecode /RunLengthDecode /RunLengthDecode /RunLengthDecode /RunLengthDecode /RunLengthDecode]
>>
stream
8181>
endstream
endobj
6 0 obj
<<
/Type /Font
/Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Encoding /MacRomanEncoding
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000079 00000 n
0000000128 00000 n
0000000189 00000 n
0000000381 00000 n
0000000600 00000 n
trailer
<<
/Size 7
/Root 1 0 R
>>
startxref
714
%%EOF

View File

@@ -59,10 +59,21 @@
{
var filters = filterProvider.GetFilters(stream.StreamDictionary);
double totalMaxEstSize = stream.Data.Length * 100;
var transform = stream.Data;
for (var i = 0; i < filters.Count; i++)
{
transform = filters[i].Decode(transform, stream.StreamDictionary, filterProvider, i);
var filter = filters[i];
totalMaxEstSize *= GetEstimatedSizeMultiplier(filter);
transform = filter.Decode(transform, stream.StreamDictionary, filterProvider, i);
if (i < filters.Count - 1 && transform.Length > totalMaxEstSize)
{
// Try to prevent malicious decompression, leading to OOM issues
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}.");
}
}
return transform;
@@ -75,15 +86,38 @@
{
var filters = filterProvider.GetFilters(stream.StreamDictionary, scanner);
double totalMaxEstSize = stream.Data.Length * 100;
var transform = stream.Data;
for (var i = 0; i < filters.Count; i++)
{
transform = filters[i].Decode(transform, stream.StreamDictionary, filterProvider, i);
var filter = filters[i];
totalMaxEstSize *= GetEstimatedSizeMultiplier(filter);
transform = filter.Decode(transform, stream.StreamDictionary, filterProvider, i);
if (i < filters.Count - 1 && transform.Length > totalMaxEstSize)
{
// Try to prevent malicious decompression, leading to OOM issues
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}.");
}
}
return transform;
}
private static double GetEstimatedSizeMultiplier(IFilter filter)
{
return filter switch
{
AsciiHexDecodeFilter => 0.5,
Ascii85Filter => 0.8,
FlateFilter or RunLengthFilter => 3,
LzwFilter => 2,
_ => 1000
};
}
/// <summary>
/// Returns an equivalent token where any indirect references of child objects are
/// recursively traversed and resolved.