Integer overflow in image buffer allocation
GHSA-g766-88fx-58h8 · CVE assignment pending
Summary
Eight image-buffer allocation paths in pkg/pdfcpu/model/image.go compute buffer sizes with unchecked int multiplication (w*h*N). On platforms with a 32-bit int, sufficiently large dimensions overflow, allocating a smaller-than-expected buffer. Today the upstream MaxImagePixels validation (computed in int64 with safemath) keeps dimensions low enough that the overflow is not reachable — but this is a defense-in-depth gap. If any image decoder ever produced output dimensions larger than those declared in the header (a pattern seen in multiple image-library CVEs), the undersized buffer would be written out of bounds, causing heap corruption.
Impact
- 64-bit platforms: not directly exploitable with current limits (overflow impossible under
MaxImagePixels), but hardening is valid. - 32-bit platforms: potentially exploitable → heap out-of-bounds write if a decoder dimension-mismatch bug exists.
- Severity: MEDIUM 5.9 — integrity impact low (heap corruption potential), availability high (process-killing panic).
Root Cause
Six primary functions (plus two variants) allocate pixel buffers with plain int multiplication:
// pkg/pdfcpu/model/image.go
buf := make([]byte, w*h*3) // writeRGBAImageBuf, writeNRGBAImageBuf
buf := make([]byte, w*h*6) // writeRGBA64ImageBuf
buf := make([]byte, w*h) // writeGrayImageBuf
buf := make([]byte, 2*w*h) // writeGray16ImageBuf
buf := make([]byte, w*h*4) // writeCMYKImageBuf
Meanwhile validateImageResourceLimits correctly uses safemath.MultiplyInt64 and MaxImagePixels = 100M — so the validation is safe but the allocation paths that trust it are not, if their inputs ever come from a source other than the validated header.
Proof of Concept
Unit-test confirmation of the overflow arithmetic (32-bit simulation):
TestImageBufferOverflowDirect/overflow_rgba_32bit
w=26768, h=26768, multiplier=3
64-bit result: 2149577472
32-bit result: -2145389824 (truncated)
→ smaller buffer allocation on 32-bit systems
Under current MaxImagePixels limits a square image is capped at ~10,000×10,000 pixels, keeping w*h*3 within 32-bit range — the fix is hardening against a future regression or decoder mismatch, matching the class of bugs seen in CVE-2026-46601 (WebP dimension mismatch) and CVE-2026-46602 (TIFF unbounded tiles).
Disclosure Timeline
| Date | Event |
|---|---|
| 2026-09-24 | Report submitted via GitHub Private Vulnerability Reporting |
| 2026-09-24 | Accepted by maintainer (hhrutter) |
| 2026-09-28 | Advisory published — GHSA-g766-88fx-58h8 |
| Fixed in | pdfcpu v0.16.0 |
Fix
Use safemath.MultiplyInt for every buffer-size computation:
// After (safe):
size, err := safemath.MultiplyInt(w, h)
if err != nil {
return nil, fmt.Errorf("image buffer size overflow: %w", err)
}
size, err = safemath.MultiplyInt(size, 3)
if err != nil {
return nil, fmt.Errorf("image buffer size overflow: %w", err)
}
buf := make([]byte, size)
Fixed in pdfcpu v0.16.0. Advisory published 2026-09-28; CVE assignment pending.