From 2cb161fa9c96b65a10972752e8c987d4f930ed79 Mon Sep 17 00:00:00 2001 From: Dojo Date: Thu, 16 Jul 2026 16:30:32 +0200 Subject: [PATCH] Fixing invalid lengths beyond hardcoded threshold --- .../PDFsharp/src/PdfSharp/Pdf.IO/Parser.cs | 20 +++++++++++--- .../tests/PdfSharp.Tests/IO/ReaderTests.cs | 27 +++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.IO/Parser.cs b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.IO/Parser.cs index 64fe1c92..886010c9 100644 --- a/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.IO/Parser.cs +++ b/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.IO/Parser.cs @@ -553,16 +553,30 @@ bool TryReadEndStream(PdfDictionary dict, SizeType streamStart, ref int streamLe var oldLength = streamLength; //_lexer.DetermineStreamLength(dict.Reference!.Position, streamLength - length, ref streamLength); - // Try to read 20 extra bytes in case reported stream length is too small. - int scanWindow = streamLength + 20; + // If this mismatch is larger than a few bytes, search up to the next object (or EOF). + // This keeps valid files fast and improves robustness for malformed /Length values. + var behindPosition = _document.IrefTable.GetPositionOfObjectBehind(dict, streamStart); + int scanWindow; + if (behindPosition != -1) + { + // Read up to next object. + scanWindow = (int)(behindPosition - streamStart); + } + else + { + // Object is the last one in the file, so read up to EOF. + scanWindow = (int)(_lexer.PdfLength - streamStart); + } + // Make sure we do not try to read beyond EOF. if (streamStart + scanWindow > _lexer.PdfLength) { // We're close to the EOF, so casting to int is OK here. scanWindow = (int)(_lexer.PdfLength - streamStart); - Debug.Assert(scanWindow >= oldLength); } + Debug.Assert(scanWindow >= oldLength); + streamLength = _lexer.DetermineStreamLength(streamStart, scanWindow, suppressObjectOrderExceptions); if (SuppressExceptions.HasError(suppressObjectOrderExceptions)) return false; diff --git a/src/foundation/src/PDFsharp/tests/PdfSharp.Tests/IO/ReaderTests.cs b/src/foundation/src/PDFsharp/tests/PdfSharp.Tests/IO/ReaderTests.cs index f27c017a..c237a2e8 100644 --- a/src/foundation/src/PDFsharp/tests/PdfSharp.Tests/IO/ReaderTests.cs +++ b/src/foundation/src/PDFsharp/tests/PdfSharp.Tests/IO/ReaderTests.cs @@ -17,6 +17,9 @@ using Xunit; using FluentAssertions; using PdfSharp.Diagnostics; +using System.Runtime.CompilerServices; +using static System.Net.Mime.MediaTypeNames; +using System.Text; #if PDFSHARP_DEBUG using static PdfSharp.Diagnostics.DebugBreakHelper; @@ -567,5 +570,29 @@ static void DrawFooter(PdfPage page, XBrush color, XRect box, string footer) gfx.DrawString(footer, font, color, box, format); } } + + [Fact] + public async Task Read_FileWithInvalidStreamLengths_Success() + { + var doc = new PdfDocument(); + var page = doc.AddPage(); + using var gfx = XGraphics.FromPdfPage(page); + + var font = new XFont(UnitTestFontResolver.ArialFont, 20, XFontStyleEx.Regular); + + gfx.DrawString(string.Concat(Enumerable.Repeat("Hello World!", 30)), font, XBrushes.Black, new XPoint(100, 100)); + + using var stream = new MemoryStream(); + await doc.SaveAsync(stream, false); + + using StreamReader reader = new StreamReader(stream, PdfSharp.Pdf.Internal.PdfEncoders.RawEncoding); + var fullFile = await reader.ReadToEndAsync(); + var invalidLengthFile = fullFile.Replace("/Length 417", "/Length 317"); + fullFile.Should().NotBe(invalidLengthFile); + MemoryStream newStream = new MemoryStream(PdfSharp.Pdf.Internal.PdfEncoders.RawEncoding.GetBytes(invalidLengthFile)); + + var doc2 = PdfReader.Open(newStream, PdfDocumentOpenMode.Import); + doc2.Should().NotBeNull(); + } } }