CVE-2026-6361
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
forcore/fxge/win32/cfx_psrenderer.cpp |
modified |
Files Changed
core/fxge/win32/cfx_psrenderer.cpp
Patch
From bce2e672827992dcd7289e2872ca10c5c20bf056 Mon Sep 17 00:00:00 2001 From: Tom Sepez <[email protected]> Date: Tue, 07 Apr 2026 15:50:30 -0700 Subject: [PATCH] Use safe arithmetic in CFX_PSRenderer::DrawDIBits() Hardening suggestion from the AI bot. Bug: 500036290 Change-Id: Ie521629d06ba944f610b941a8c9e9505fa29aea7 Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/145731 Reviewed-by: Lei Zhang <[email protected]> Commit-Queue: Tom Sepez <[email protected]> --- diff --git a/core/fxge/win32/cfx_psrenderer.cpp b/core/fxge/win32/cfx_psrenderer.cpp index b38f1a2..b8710e5 100644 --- a/core/fxge/win32/cfx_psrenderer.cpp +++ b/core/fxge/win32/cfx_psrenderer.cpp @@ -620,8 +620,16 @@ encoder_iface_->pJpegEncodeFunc(bitmap, &output_buf, &output_size)) { filter = "/DCTDecode filter "; } else { - int src_pitch = width * bytes_per_pixel; - output_size = height * src_pitch; + FX_SAFE_UINT32 safe_pitch = bytes_per_pixel; + safe_pitch *= width; + FX_SAFE_UINT32 safe_output_size = safe_pitch; + safe_output_size *= height; + if (!safe_output_size.IsValid()) { + WriteString("\nQ\n"); + return false; + } + uint32_t src_pitch = safe_pitch.ValueOrDie(); + output_size = safe_output_size.ValueOrDie(); output_buf = FX_Alloc(uint8_t, output_size); for (int row = 0; row < height; row++) { const uint8_t* src_scan = bitmap->GetScanline(row).data();
Original Bug Report
Potential Integer Overflow leading to OOB Write in CFX_PSRenderer::DrawDIBits
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the security team.
Overview: A potential integer overflow exists in PDFium’s PostScript renderer when calculating the memory required for image conversion. When printing a specially crafted PDF with massive dimensions, this calculation overflows, leading to a severely undersized heap allocation. Subsequent rendering loops write attacker-controlled data extensively out-of-bounds, potentially allowing for remote code execution within the sandboxed utility process.
Affected files:
third_party/pdfium/core/fxge/win32/cfx_psrenderer.cppthird_party/pdfium/core/fpdfapi/page/cpdf_pageimagecache.cppthird_party/pdfium/core/fpdfapi/page/cpdf_dib.cpp
Estimated timestamp from git blame: 2024-08-09
Description
A potential integer overflow vulnerability exists in CFX_PSRenderer::DrawDIBits (third_party/pdfium/core/fxge/win32/cfx_psrenderer.cpp). When a PDF is printed to a PostScript printer, this function allocates a heap buffer to hold converted image data. The calculation for the buffer size relies on standard 32-bit signed integer math (int). If an image is sufficiently large, the mathematical product of height and src_pitch exceeds INT_MAX, wraps around, and truncates to a small positive value, resulting in an undersized buffer allocation.
Vulnerability Details
In CFX_PSRenderer::DrawDIBits, the buffer size is calculated without safe math wrappers:
int src_pitch = width * bytes_per_pixel;
output_size = height * src_pitch;
output_buf = FX_Alloc(uint8_t, output_size);
While upstream PDFium checks (like in CPDF_DIB::LoadInternal) validate image dimensions using FX_SAFE_UINT32, an attacker can bypass these limitations by utilizing an image configuration that expands later in the pipeline. For example, a 16-bit Grayscale image with dimensions 11,075 x 131,071 requires approximately 2.9 GB uncompressed, which fits perfectly within a valid 32-bit unsigned integer.
Because this image exceeds the 60 MB kHugeImageSize caching threshold (CPDF_PageImageCache::Entry::ContinueGetCachedBitmap), PDFium evaluates it lazily (realize_hint=false), avoiding an immediate allocation.
When CFX_PSRenderer::DrawDIBits processes the image, the 16-bpc format is promoted to a 24-bpp BGR format (bytes_per_pixel = 3). The renderer calculates src_pitch as 11,075 * 3 = 33,225. It then calculates output_size as 131,071 * 33,225, which equals 4,354,833,975. This value overflows a 32-bit signed int and truncates to exactly 59,866,679 bytes. FX_Alloc subsequently allocates this severely undersized 59.8 MB buffer.
In the subsequent copy loop:
for (int row = 0; row < height; row++) {
uint8_t* dest_scan = UNSAFE_TODO(output_buf + row * src_pitch);
// ... copies pixels to dest_scan
}
- Forward OOB Write: Starting around row 1,802, the offset
row * 33,225exceeds the allocated 59.8 MB, causing massive forward out-of-bounds writes of attacker-controlled pixel data. - Backward OOB Write: At row 64,635, the signed 32-bit integer offset calculation overflows
INT_MAX(64,635 * 33,225 = 2,147,497,875), wrapping to a negative value (-2,147,469,421). This negative offset is sign-extended and added to theoutput_bufpointer, resulting in a backward out-of-bounds write roughly 2.14 GB before the buffer.
Potential Attack Steps
Note: These are potential steps based on code analysis, as our tooling agent cannot execute code to verify a working proof of concept.
- An attacker crafts a malicious PDF containing an
ImageXObject. - The image specifies
Width= 11,075,Height= 131,071,BitsPerComponent= 16, andColorSpace=DeviceGray, backed by aFlateDecodestream containing carefully structured malicious pixel payloads. - The attacker convinces a victim to open the PDF in Chrome and print it to a PostScript-capable printer on a Windows machine.
- PDFium processes the image lazily due to its large uncompressed footprint (~2.9GB).
- During PostScript rendering, the image is promoted to 24-bpp, triggering the integer overflow in
CFX_PSRenderer::DrawDIBits. - The rendering loop copies the attacker’s decompressed pixel stream wildly out of bounds.
- While writing across gigabytes of memory is likely to hit an unmapped guard page and crash, an attacker might exploit race conditions by overwriting critical structures (like IPC objects or vtables) accessed by concurrent threads within the
kPdfConversionutility process before the crash occurs, potentially achieving Remote Code Execution.
Suggested Fix
The memory calculation in CFX_PSRenderer::DrawDIBits should utilize FX_SAFE_UINT32 to prevent integer overflow and abort rendering if the required size exceeds representable limits.
FX_SAFE_UINT32 safe_src_pitch = width;
safe_src_pitch *= bytes_per_pixel;
FX_SAFE_UINT32 safe_output_size = safe_src_pitch;
safe_output_size *= height;
if (!safe_output_size.IsValid()) {
return false; // or handle error accordingly
}
int src_pitch = safe_src_pitch.ValueOrDie();
size_t output_size = safe_output_size.ValueOrDie();
Evaluated with Chrome root at commit: f200f57a19490707ff8bc7aa5de3cbc443a3afad
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.