CVE-2026-6305
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
forthird_party/lcms/0036-cubesize-overflow.patch |
modified | |
forthird_party/lcms/src/cmslut.c |
modified |
Files Changed
third_party/lcms/0036-cubesize-overflow.patchthird_party/lcms/README.pdfiumthird_party/lcms/src/cmslut.c
Patch
From 237bc015b08696881bf0a2007bec95d0aecf8e67 Mon Sep 17 00:00:00 2001 From: Lei Zhang <[email protected]> Date: Thu, 26 Mar 2026 15:09:23 -0700 Subject: [PATCH] Patch an overflow in Little CMS Apply fix [1] from upstream, which is not in the most recent versioned release. [1] https://github.com/mm2/Little-CMS/commit/e0641b1828d0a1af5ecb1b11fe22f24fceefd4bc Bug: 496618639 Change-Id: I89309d9cddf15c053cdffed341e50e5616e68b74 Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/145450 Reviewed-by: Tom Sepez <[email protected]> Commit-Queue: Lei Zhang <[email protected]> --- diff --git a/third_party/lcms/0036-cubesize-overflow.patch b/third_party/lcms/0036-cubesize-overflow.patch new file mode 100644 index 0000000..3540330 --- /dev/null +++ b/third_party/lcms/0036-cubesize-overflow.patch @@ -0,0 +1,28 @@ +commit e0641b1828d0a1af5ecb1b11fe22f24fceefd4bc +Author: Marti Maria <[email protected]> +Date: Thu Mar 12 22:57:35 2026 +0100 + + check for overflow + + Thanks to Guanni Qu for detecting & reporting the issue + +diff --git a/src/cmslut.c b/src/cmslut.c +index a60baf9..65c935f 100644 +--- a/src/cmslut.c ++++ b/src/cmslut.c +@@ -468,12 +468,12 @@ cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b) + for (rv = 1; b > 0; b--) { + + dim = Dims[b-1]; +- if (dim <= 1) return 0; // Error +- +- rv *= dim; ++ if (dim <= 1) return 0; + + // Check for overflow + if (rv > UINT_MAX / dim) return 0; ++ ++ rv *= dim; + } + + // Again, prevent overflow diff --git a/third_party/lcms/README.pdfium b/third_party/lcms/README.pdfium index 2a27932..2f27874 100644 --- a/third_party/lcms/README.pdfium +++ b/third_party/lcms/README.pdfium @@ -25,3 +25,4 @@ 0033-opt-integer-overflow.patch: Protect against integer overflow. 0034-dead-code.patch: Remove dead code. 0035-func-ptr-mixup.patch: Prevent mixing up function pointer types. +0036-cubesize-overflow.patch: Check for overflow in CubeSize(). diff --git a/third_party/lcms/src/cmslut.c b/third_party/lcms/src/cmslut.c index 22a16b3..11361de 100644 --- a/third_party/lcms/src/cmslut.c +++ b/third_party/lcms/src/cmslut.c @@ -467,12 +467,12 @@ for (rv = 1; b > 0; b--) { dim = Dims[b-1]; - if (dim <= 1) return 0; // Error - - rv *= dim; + if (dim <= 1) return 0; // Check for overflow if (rv > UINT_MAX / dim) return 0; + + rv *= dim; } return rv;
Original Bug Report
Heap-buffer-overflow in PDFium via integer overflow in bundled lcms2 ICC CLUT allocation
Summary
An integer overflow in the bundled lcms2 color management library allows a crafted ICC profile embedded in a PDF to trigger a heap-buffer-overflow read inside the Chromium renderer process. The overflow occurs in CubeSize(), which computes the total number of entries in a multi-dimensional color lookup table (CLUT). By constructing a five-dimensional CLUT through an ICC v4 multiProcessElements (MPE) pipeline, an attacker can cause the entry count to wrap around 32 bits while passing the existing overflow check, resulting in a drastically undersized heap allocation. The interpolation strides, computed from the original unwrapped grid dimensions, then index far past the end of the buffer during normal color transformation. The bug affects all platforms and requires no user interaction beyond opening a PDF.
Bisect
Introducing Commit: 0bd847232 (PDFium)
- Date: 2017-08-14
- Author: Nicolas Pena
- Review: LCMS: upgrade to 2.8
The unchecked outputChan * CubeSize(...) multiplication has been present since lcms was first bundled into PDFium. A post-multiply overflow check was added to CubeSize() itself in a later lcms upgrade, but that check is bypassable with carefully chosen five-dimensional grid sizes, and the outer multiplication by outputChan was never checked at all.
Root Cause
The function CubeSize() in cmslut.c computes the product of all grid dimensions for a CLUT:
// third_party/pdfium/third_party/lcms/src/cmslut.c
cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b)
{
cmsUInt32Number rv, dim;
for (rv = 1; b > 0; b--) {
dim = Dims[b-1];
if (dim <= 1) return 0;
rv *= dim;
if (rv > UINT_MAX / dim) return 0;
}
return rv;
}
The overflow check on the last line executes after the multiplication has already been performed. When the 32-bit product wraps to a value that happens to be less than or equal to UINT_MAX / dim, the check passes despite the overflow. This is exploitable with five-dimensional grid sizes because the larger search space makes it practical to find dimension tuples whose cumulative product wraps to a small value while never triggering the post-multiply guard at any intermediate step.
A concrete example is the grid dimensions [255, 161, 61, 245, 7]. The true product is 4,295,968,825, which exceeds 2^32. At each step of the loop the post-multiply check compares the wrapped running total against UINT_MAX / dim and finds it acceptable, so CubeSize returns 1529 instead of the correct value.
The caller in cmsStageAllocCLutFloatGranular then multiplies by the output channel count without any further overflow check:
// third_party/pdfium/third_party/lcms/src/cmslut.c
// There is a potential integer overflow on conputing n and nEntries.
NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan);
The comment acknowledges the risk but no mitigation follows. With outputChan = 3 and CubeSize returning 1529, the allocation is n = 4587 floats, or 18,348 bytes.
Independently, _cmsComputeInterpParamsEx derives the interpolation strides from the original, unwrapped grid sizes:
// third_party/pdfium/third_party/lcms/src/cmsintrp.c
p -> opta[0] = p -> nOutputs;
for (i=1; i < InputChan; i++)
p ->opta[i] = p ->opta[i-1] * nSamples[InputChan-i];
For the grid [255, 161, 61, 245, 7] with three output channels, this produces strides opta = {3, 21, 5145, 313845, 50529045}. The stride opta[2] = 5145 already exceeds the total allocation of 4587 entries, so any CLUT evaluation that steps into the third grid dimension reads past the heap buffer.
The attacker reaches this code through PDFium’s ICC profile loading. A PDF declares a four-component ICCBased colorspace and embeds an ICC v4 profile whose DToB0 tag contains an MPE pipeline with two stages: a 4-to-5 channel matrix followed by a 5-to-3 channel CLUT. The matrix stage is permitted by Type_MPEmatrix_Read, which accepts any channel count below cmsMAXCHANNELS. The overall pipeline header declares 4 input and 3 output channels, matching the CMYK colorspace, so Type_MPE_Read’s final consistency check passes. When PDFium calls cmsCreateTransform, lcms reads the DToB0 tag (which takes priority over AToB0 for ICC v4 profiles), constructs the pipeline with the undersized CLUT, and evaluates it during the transform’s internal cache initialization. The 5D tetrahedral interpolation in Eval5InputsFloat then dereferences indices computed from the oversized strides, reading 2268 bytes past the end of the 18,348-byte allocation.
Reproduce
Tested on Chromium commit 711b2435c7f04297b62a57826cb5da203f4c18a4 on macOS arm64 and Ubuntu 22.04. The bug is platform-independent.
Check out the commit and configure an ASAN build:
is_asan = true
is_debug = false
dcheck_always_on = false
Then build:
autoninja -C out/asan chrome
Generate the malicious PDF using the attached Python script, or use the PDF file(poc.pdf) I uploaded directly, then open it in Chrome:
python3 gen_poc.py
out/asan/Chromium.app/Contents/MacOS/Chromium --user-data-dir=./userdata poc.pdf
The renderer process will abort with ERROR: AddressSanitizer: heap-buffer-overflow in TetrahedralInterpFloat, reading 2268 bytes past the end of an 18348-byte CLUT allocation. The handle_segv=2 option ensures ASAN handles the signal before Crashpad. No source modifications are required. The full ASAN trace is in asan.txt.
ASAN output
=================================================================
==79778==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6290000d7288 at pc 0x00035b0a3470 bp 0x00016fb501d0 sp 0x00016fb501c8
READ of size 4 at 0x6290000d7288 thread T0
==79778==WARNING: invalid path to external symbolizer!
==79778==WARNING: Failed to use and restart external symbolizer!
#0 0x00035b0a346c in TetrahedralInterpFloat+0x76c (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/148.0.7757.0/Chromium Framework:arm64+0x110a746c)
#1 0x00035b0a5470 in Eval4InputsFloat+0x1fc (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/148.0.7757.0/Chromium Framework:arm64+0x110a9470)
#2 0x00035b0a6c0c in Eval5InputsFloat+0x1fc (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/148.0.7757.0/Chromium Framework:arm64+0x110aac0c)
#3 0x00035b0bc720 in _LUTeval16+0x548 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/148.0.7757.0/Chromium Framework:arm64+0x110c0720)
#4 0x00035b0f7dd0 in cmsCreateExtendedTransform+0x880 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/148.0.7757.0/Chromium Framework:arm64+0x110fbdd0)
#5 0x00035b0f9580 in cmsCreateTransform+0x250 (/Users/test/Desktop/src/chromium/src/out/asan/Chromium.app/Contents/Frameworks/Chromium Framework.framework/Versions/148.0.7757.0/Chromium Framework:arm64+0x110fd580)
......
The complete untruncated log is in the attached asan.log.
References
- cmslut.c CubeSize
- cmslut.c cmsStageAllocCLutFloatGranular nEntries overflow
- cmsintrp.c opta stride computation
- cmstypes.c Type_MPEclut_Read
- icc_transform.cpp CreateTransformSRGB
Credit
Please use 86ac1f1587b71893ed2ad792cd7dde32 as the credit for this vulnerability. Thank you.
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/pdfium/core/fxcodec/icc/icc_transform.cpp;l=56
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/pdfium/third_party/lcms/src/cmsintrp.c;l=144
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/pdfium/third_party/lcms/src/cmslut.c;l=461
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/pdfium/third_party/lcms/src/cmslut.c;l=666
- https://source.chromium.org/chromium/chromium/src/+/main:third_party/pdfium/third_party/lcms/src/cmstypes.c;l=4422