Skip to content

Harden PNG PDF image decoding#1933

Closed
PrzemyslawKlys wants to merge 1 commit into
masterfrom
propose-fix-for-pdf-export-dos
Closed

Harden PNG PDF image decoding#1933
PrzemyslawKlys wants to merge 1 commit into
masterfrom
propose-fix-for-pdf-export-dos

Conversation

@PrzemyslawKlys

Copy link
Copy Markdown
Member

Motivation

  • The PDF image pipeline accepted additional PNG variants but performed unbounded Flate decompression and unchecked dimension arithmetic, allowing small malicious PNGs to declare huge dimensions or deflate bombs and cause OOM/OverflowExceptions.
  • Adam7 interlace normalization allocated full-image buffers from untrusted IHDR width/height before validating decompressed payload and no PNG CRCs were checked.

Description

  • Add size/cap limits and safe decode entrypoint: introduce FlateDecoder.TryDecode and streaming copy logic to enforce a maximum decompressed byte cap and avoid unbounded MemoryStream growth (file: OfficeIMO.Pdf/Reading/Filters/FlateDecoder.cs).
  • Enforce resource checks and checked arithmetic: add constants (MaxPngPixelCount, MaxPngExpandedBytes, MaxPngDecodedBytes) and helpers TryGetPngCheckedLength, TryGetPngScanlineLength, TryGetPngRowByteCount, and TryValidatePngResourceLimits and use them before any large allocation (file: OfficeIMO.Pdf/Rendering/Writer/PdfWriter.Images.cs).
  • Validate PNG chunks and avoid expansion on bad inputs: validate CRC for each PNG chunk before processing and reject invalid CRCs (file: OfficeIMO.Pdf/Rendering/Writer/PdfWriter.Images.cs).
  • Make image expansion robust: update Adam7 normalization and 16-bit/packed/indexed/alpha PNG expansion paths to call TryDecodePngData (capped decompression) and the checked-length helpers so allocations are bounded and validated prior to use (files: PdfWriter.Images.Interlace.cs, PdfWriter.Images.Png16.cs, PdfWriter.Images.cs).
  • Tests and fixtures: add regression tests for oversized interlaced PNGs and invalid CRCs and update PNG test helpers to emit valid CRCs when constructing test PNGs (files: OfficeIMO.Tests/Pdf/PdfDocumentPngImageTests.cs, OfficeIMO.Tests/Pdf/PdfPngTestImages.cs, OfficeIMO.Tests/Pdf/PdfDocumentImageValidationTests.cs, OfficeIMO.Tests/Pdf/PdfStamperSupport.cs).

Testing

  • Restored and built the solution and PDF project: ran dotnet restore OfficeIMO.sln and dotnet build OfficeIMO.sln --framework net8.0 --no-restore, dotnet build OfficeIMO.Pdf/OfficeIMO.Pdf.csproj --framework net10.0 --no-restore, and dotnet build OfficeIMO.Pdf/OfficeIMO.Pdf.csproj --framework netstandard2.0 --no-restore and they completed successfully.
  • Ran targeted unit tests under net8.0: dotnet test OfficeIMO.Tests/OfficeIMO.Tests.csproj --framework net8.0 --filter FullyQualifiedName~OfficeIMO.Tests.Pdf.PdfDocumentPngImageTests passed (14 tests) and dotnet test OfficeIMO.Tests/OfficeIMO.Tests.csproj --framework net8.0 --filter FullyQualifiedName~OfficeIMO.Tests.Pdf.PdfDocumentImageValidationTests passed (61 tests).
  • Verified the build after changes: dotnet build OfficeIMO.sln --framework net8.0 --no-restore succeeded and git diff --check produced no whitespace/line issues worth blocking.

If you want, I can split the large PdfWriter.Images.cs into smaller helper files or tune the resource limits (MaxPngPixelCount, MaxPngDecodedBytes, MaxPngExpandedBytes) to match stricter or looser policies for your deployment.


Codex Task

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ce611a3e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

private static bool IsPngChunkCrcValid(byte[] data, int chunkOffset, int chunkLength) {
int crcOffset = chunkOffset + 8 + chunkLength;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard CRC offsets with wide arithmetic

When a malformed PNG advertises a very large positive chunk length after the required chunks, chunkOffset + 8 + chunkLength can overflow to a negative int. The following bounds check then passes and ReadUInt32BigEndian indexes with a negative offset, so public image validation can throw instead of rejecting the PNG. Use long/checked arithmetic for the CRC offset before reading the CRC bytes.

Useful? React with 👍 / 👎.

Comment on lines +52 to +54
if (data.Length <= maxOutputBytes) {
output = data;
return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop falling back after hitting the inflate limit

When a PNG IDAT/deflate stream expands beyond MaxPngDecodedBytes, TryCopyToByteArray returns false, but this fallback still returns the compressed input whenever the compressed size is under the limit. The PNG callers then treat those bytes as decoded scanlines, so a crafted oversized payload with a legal filter byte can bypass the new decompressed-size rejection instead of failing; track a limit-exceeded result separately and do not fall back in that case.

Useful? React with 👍 / 👎.

Comment on lines +54 to +56
if (!IsPngChunkCrcValid(data, offset, length)) {
unsupportedReason = "PNG chunk CRC is invalid.";
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update remaining zero-CRC PNG fixtures

In test paths that still use the old literal PNG helpers with zero chunk CRCs (for example PdfDocumentCanvasTests.CreateMinimalRgbPng, which is passed to canvas.Image(...)), this new unconditional CRC check rejects the fixture before rendering. This commit only updates some helpers, so the unrelated PDF image/canvas/extractor tests that reuse the remaining literals will start failing until those fixtures get valid CRCs too.

Useful? React with 👍 / 👎.

@PrzemyslawKlys

Copy link
Copy Markdown
Member Author

Superseded by #1934, which includes the PNG PDF image decoding hardening along with the broader document parsing security pass and has merged cleanly into master with CI green. Closing this failing one-off Codex task PR to keep the security queue focused on the consolidated fix.

@PrzemyslawKlys PrzemyslawKlys deleted the propose-fix-for-pdf-export-dos branch June 17, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant