ANT-2026-0BETG8WD · poppler

heap-buffer-overflow high

Severity Claude high · Security research firm high · Maintainer -

Discovered by Claude Mythos Preview

REPORT

Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Ada Logics.

ANT-2026-0BETG8WD: Heap-buffer-overflow in SplashOutputDev::unsetSoftMaskFromImageMask at SplashOutputDev.cc:2688 via crafted PDF tiling pattern with image mask

In poppler's Splash rendering pipeline, SplashOutputDev::tilingPatternFill() allocates a bitmap sized to the pattern tile (line 4361), but setSoftMaskFromImageMask() sizes its softmask to the main page-level bitmap (line 2662). During teardown, unsetSoftMaskFromImageMask() copies the softmask into the tile bitmap's alpha channel using the softmask's larger dimensions as loop bounds (line 2688: dest[c] = src[c]), writing past the end of the heap-allocated tile alpha buffer. An attacker who controls the ratio of page dimensions to tile dimensions in a crafted PDF controls the overflow length. The result is a reliable crash and potential heap-metadata/adjacent-object corruption in any poppler-based viewer or server-side pipeline using the Splash backend.

Target

Project: poppler
Location: SplashOutputDev.cc:2688, unsetSoftMaskFromImageMask()

Technical Details

The tile bitmap's alpha buffer is sized to tile dimensions while the copy loop in unsetSoftMaskFromImageMask() iterates over the (larger) softmask dimensions derived from the main rendering surface — a classic size-source mismatch. ASAN reports a WRITE "0 bytes after 1-byte region", confirming that even a 1-byte tile allocation is overflowed by the page-sized copy loop; the entire fault stack is internal to poppler (SplashOutputDev.cc → Gfx.cc → Page.cc → PDFDoc.cc).

Crash trace (truncated — full trace in attached crash.log):

INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 3169505516
INFO: Loaded 1 modules   (215908 inline 8-bit counters): 215908 [0x6209f9524340, 0x6209f9558ea4), 
INFO: Loaded 1 PC tables (215908 PCs): 215908 [0x6209f9558ea8,0x6209f98a44e8), 
/out/page_label_fuzzer: Running 1 inputs 1 time(s) each.
Running: /tmp/poc
EXIT_CODE:1


=== ASAN Report ===
=================================================================
==27==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x770e152e30d1 at pc 0x6209f842429f bp 0x7ffdaa704770 sp 0x7ffdaa704768
WRITE of size 1 at 0x770e152e30d1 thread T0
    #0 0x6209f842429e in SplashOutputDev::unsetSoftMaskFromImageMask(GfxState*, std::__1::array<double, 6ul>&) /src/poppler/poppler/SplashOutputDev.cc:2688:21
    #1 0x6209f81a92a2 in Gfx::doPatternImageMask(Object*, Stream*, int, int, bool, bool) /src/poppler/poppler/Gfx.cc:2016:10
    #2 0x6209f81bb6f5 in Gfx::doImage(Object*, Stream*, bool) /src/poppler/poppler/Gfx.cc:4347:17
    #3 0x6209f817877d in Gfx::opXObject(Object*, int) /src/poppler/poppler/Gfx.cc:4157:13
    #4 0x6209f819f270 in Gfx::go(Gfx::DisplayType) /src/poppler/poppler/Gfx.cc:649:13
    #5 0x6209f819e680 in Gfx::display(Object*, Gfx::DisplayType) /src/poppler/poppler/Gfx.cc:606:5
    #6 0x6209f8435c82 in SplashOutputDev::tilingPatternFill(GfxState*, Gfx*, Catalog*, GfxTilingPattern*, std::__1::array<double, 6ul> const&, int, int, int, int, double, double) /src/poppler/poppler/SplashOutputDev.cc:4387:10
    #7 0x6209f81a6419 in Gfx::doTilingPatternFill(GfxTilingPattern*, bool, bool, bool) /src/poppler/poppler/Gfx.cc:2217:53
    [... 36 more frames — full trace in crash.log]

Reproduction

  1. Craft a PDF containing a tiling pattern with an embedded image mask, choosing page dimensions larger than tile dimensions to control overflow length
  2. Deliver the PDF to the victim via email, download, upload-for-processing, etc.
  3. Victim opens or processes the PDF in a poppler-based application using the Splash backend
  4. tilingPatternFill() allocates a small tile-sized bitmap; setSoftMaskFromImageMask() then creates a softmask sized to the main page bitmap
  5. unsetSoftMaskFromImageMask() copies the oversized softmask into the undersized tile alpha buffer, writing past the heap allocation

[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]

Acknowledgement

This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-0BETG8WD.


Reference: ANT-2026-0BETG8WD
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure

SECURITY RESEARCH FIRM ANALYSIS

Triage and disclosure were performed by Ada Logics.

Verdict
true positive
Severity
high
TIMELINE

Dates from discovery through public reveal.

  1. 2026-03-20 Reported to tracker
  2. 2026-05-14 Patch released
  3. 2026-05-20 Sent to maintainer
  4. 2026-08-17 Publicly revealed
PROVENANCE

SHA-3-512 hash:

396d3abb80ceb62364bcf81905d621fb58a2551cd2741cf303804ff0eb2abce5be0f94658266721a4be6b2de84a8652566ef6fc1cc2902c607532e7d94e76e54

Committed 2026-03-23 21:57 PT

Revealed 2026-08-17 14:15 PT

Verify (download preimage.json)

Show preimage JSON
{
  "ant_id": "ANT-2026-0BETG8WD",
  "bug_class": "heap-buffer-overflow",
  "claude_severity": "high",
  "commit_sha": null,
  "created_at": "2026-03-20T19:13:13+00:00",
  "description": "In poppler's Splash rendering pipeline, SplashOutputDev::tilingPatternFill() allocates a bitmap sized to the pattern tile (line 4361), but setSoftMaskFromImageMask() sizes its softmask to the main page-level bitmap (line 2662). During teardown, unsetSoftMaskFromImageMask() copies the softmask into the tile bitmap's alpha channel using the softmask's larger dimensions as loop bounds (line 2688: dest[c] = src[c]), writing past the end of the heap-allocated tile alpha buffer. An attacker who controls the ratio of page dimensions to tile dimensions in a crafted PDF controls the overflow length. The result is a reliable crash and potential heap-metadata/adjacent-object corruption in any poppler-based viewer or server-side pipeline using the Splash backend.",
  "discovered_at": null,
  "location": "SplashOutputDev.cc:2688, unsetSoftMaskFromImageMask()",
  "poc_sha256": "5df82ac99d754ff1f89333c9f0a34d2f4316fc59ffb89fad5d34a346528728f0",
  "preimage_version": 1,
  "project": "poppler",
  "reproduction": [
    "1. Craft a PDF containing a tiling pattern with an embedded image mask, choosing page dimensions larger than tile dimensions to control overflow length",
    "2. Deliver the PDF to the victim via email, download, upload-for-processing, etc.",
    "3. Victim opens or processes the PDF in a poppler-based application using the Splash backend",
    "4. tilingPatternFill() allocates a small tile-sized bitmap; setSoftMaskFromImageMask() then creates a softmask sized to the main page bitmap",
    "5. unsetSoftMaskFromImageMask() copies the oversized softmask into the undersized tile alpha buffer, writing past the heap allocation"
  ],
  "technical_details": "The tile bitmap's alpha buffer is sized to tile dimensions while the copy loop in unsetSoftMaskFromImageMask() iterates over the (larger) softmask dimensions derived from the main rendering surface — a classic size-source mismatch. ASAN reports a WRITE \"0 bytes after 1-byte region\", confirming that even a 1-byte tile allocation is overflowed by the page-sized copy loop; the entire fault stack is internal to poppler (SplashOutputDev.cc → Gfx.cc → Page.cc → PDFDoc.cc).",
  "title": "Heap-buffer-overflow in SplashOutputDev::unsetSoftMaskFromImageMask at SplashOutputDev.cc:2688 via crafted PDF tiling pattern with image mask",
  "vendor_severity": "high"
}