See how little cms compares to other vendors in security performance
Little CMS (lcms2) 2.16 through 2.18 before 2.19 has an integer overflow in ParseCube in cmscgats.c.
Abhinav Agarwal <abhinavagarwal1996 () gmail com> writes: A 992-byte PDF crashes a bunch of stock Ubuntu 24.04 consumers: evince-thumbnailer, Poppler (pdftoppm / pdftocairo / pdfimages), the cups-filters PDF-to-raster print filter, Okular, and GIMP's PDF plug-in all segfault inside liblcms2. OpenJDK 21 on Ubuntu crashes too, and Windows Temurin 21.0.9 crashes in its bundled lcms.dll (3/3 independent runs). There's also a coarse seed- correlated heap-read primitive on Linux glibc with ASLR off - a real CWE-200 channel, though not a generic arbitrary read. Upstream fixed it on master in February/March but hasn't cut a release, no advisory, no CVE. The GHSA I filed was closed without a reply. Looking for a CVE and for distro attention.
[...]
Timeline --------
2010-10 CubeSize() check-after-multiply pattern introduced. 2026-02-19 Fix 1: da6110b. 2026-03-12 Fix 2: e0641b1. 2026-04-13 GHSA-4xp6-rcgg-m9qq filed (private advisory). 2026-04-14 MITRE CVE request filed (CVE Request 2025002). Submitted with the evidence that existed at the time. 2026-04-16 Asked the maintainer on the GHSA whether he'd triage, told him I'd publish otherwise. 2026-04-17 GHSA closed without engagement. Public disclosure Upstream have amended their policy now [0]: Please contact me instead. Security advisories are immediatly deleted without checking due to high level of SPAM received. [0] https://github.com/mm2/Little-CMS/commit/5afc7476582b29a2b3f967a1999cf14d60a93943
There have also been two fixes in master that didn't come up here: 'A try to get rid of spam reports about "vulnerabilities" that are not real.' (https://github.com/mm2/Little-CMS/commit/429ea284550f1925d5b1b4b9ef901dfd62031158)
'Add guard on integer overflow when reading .cube files' (https://github.com/mm2/Little-CMS/commit/704896b7d690a0f31845d9622681058e812e9b53)
I have not analysed either. [...] sam
Three updates:
(1) NVD has rescored CVE-2026-41254 to 7.5 (High) from the initial CNA score of 4.0 (Medium) AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H: https://nvd.nist.gov/vuln/detail/CVE-2026-41254
(2) There is an additional adjacent security relevant finding (medium sev) "ParseCube integer overflow in LUT allocation" which was similarly fixed by upstream without CVE and ghsa was closed without substantial engagement. CVE from MITRE has been requested. https://github.com/mm2/Little-CMS/commit/6a68601
(2) Upstream has shipped the stable release with fix (covers both CVE-2026-41254 and pending CVE for (2)): lcms2.19 (2026-04-24).
-- Abhinav Agarwal
On Sat, Apr 18, 2026 at 12:46 AM Abhinav Agarwal <abhinavagarwal1996 () gmail com> wrote: MITRE assigned CVE-2026-41254 on 2026-04-17
-- Abhinav Agarwal
On Fri, Apr 17, 2026 at 2:28 PM Abhinav Agarwal <abhinavagarwal1996 () gmail com> wrote: A 992-byte PDF crashes a bunch of stock Ubuntu 24.04 consumers: evince-thumbnailer, Poppler (pdftoppm / pdftocairo / pdfimages), the cups-filters PDF-to-raster print filter, Okular, and GIMP's PDF plug-in all segfault inside liblcms2. OpenJDK 21 on Ubuntu crashes too, and Windows Temurin 21.0.9 crashes in its bundled lcms.dll (3/3 independent runs). There's also a coarse seed- correlated heap-read primitive on Linux glibc with ASLR off - a real CWE-200 channel, though not a generic arbitrary read. Upstream fixed it on master in February/March but hasn't cut a release, no advisory, no CVE. The GHSA I filed was closed without a reply. Looking for a CVE and for distro attention.
Full write-up: https://abhinavagarwal07.github.io/posts/lcms2-cubesize-overflow/
Reachability (Ubuntu 24.04 LTS stock, liblcms2-2 2.14-2build1; Windows Server 2022 with Temurin 21.0.9) --------------------------------------------------------------------------
SEGV, no local code changes:
tumblerd (D-Bus auto-activated thumbnail service). tumblerd is the freedesktop thumbnail daemon that ships as the default on Xfce and is available on GNOME as a fallback; its bundled tumbler-poppler-thumbnailer.so plugin loads libpoppler + liblcms2 directly into the daemon process. A single dbus-send "Queue" call with the PDF's URI is enough: tumblerd was not running beforehand and wasn't on $PATH, but D-Bus auto-activated the service on the Queue call, the service pulled the PDF, and the daemon SIGSEGV'd in liblcms2.so.2.0.14. Reproduced 4/4, with kernel segfault ... in liblcms2.so.2.0.14 and apport records. This is the same D-Bus call that a file manager issues when a directory is opened, so the real-world shape is "open a folder containing the PDF, the system's thumbnail daemon dies."
Direct evince-thumbnailer CLI (evince-thumbnailer -s 200 poc.pdf out.jpg) crashes the same way (SEGV at liblcms2.so.2.0.14+0xb503, Eval4Inputs+643, cmsintrp.c:909).
SHA256 (pociccbased5ch.pdf): 5c328a4362185c6dca2d6cae13c74ed456889798220f3f16e840449648121b55
Poppler: pdftoppm, pdftocairo, pdfimages -list. Same 992-byte PDF with a 1x1 image XObject using /ColorSpace [/ICCBased 5 0 R]. Poppler warns on N>4 and does not abort; goes on to call cmsCreateTransform(). Same crash site.
Okular 4:23.08.5 (xvfb-run). SEGV via okularGeneratorpoppler.so -> libpoppler-qt5 -> lcms2. Kernel: Okular::PixmapG[PID]: segfault ... in liblcms2.so.2.0.14[0xb503], Eval4Inputs+643. Core file + gdb backtrace captured.
cups-filters pdftoraster 2.0.0-0ubuntu4.1. This is the CUPS PDF-to-raster filter. It lives at /usr/lib/cups/filter/pdftoraster rather than on $PATH, so which pdftoraster misses it - invoke it the way CUPS does: /usr/lib/cups/filter/pdftoraster 1 root "" 1 "" < poc.pdf. Kernel: pdftoraster[PID]: segfault ... in liblcms2.so.2.0.14[0xb503]. Core + gdb backtrace captured in the primary-evidence bundle.
GIMP 2.10.36-3 file-pdf-load plug-in (under xvfb-run, headless batch mode). The plug-in subprocess SIGSEGVs. GIMP installs its own signal handler, so the usual kernel dmesg line doesn't appear, but strace catches SIGSEGV{sicode=SEGVACCERR} at fault time, and the frame-by-frame proof comes from running the same PDF through evince-thumbnailer under gdb - identical poppler + lcms2 library chain.
LibreOffice import: inconsistent enough that I'd treat it as a secondary target rather than cite it as confirmed. On the authoritative fresh-VM run under script(1), LO rejected the PDF at the load stage before ever calling into lcms2. On a separate VM earlier, xpdfimport crashed with a matching dmesg line. Both outcomes reproduce; I can't point at a single reliable command that crashes LO the way the other rows do.
Flask+Docker PDF thumbnailer spawning pdftoppm returns HTTP 500 (exitcode:-11) per upload. Same shape as any Poppler-backed webmail preview, DMS thumbnailer, or CI artifact renderer.
OpenJDK 21 on Ubuntu. ICCProfile.getInstance() + ICCColorSpace.toRGB(). SEGV in system liblcms2.so.2. Confirmed with both the 18 MB 7CLR profile and a 4,819-byte 5CLR variant (JdkPoc5.java, input array sized via getNumComponents()).
OpenJDK 21 Temurin 21.0.9 on Windows Server 2022. EXCEPTIONACCESSVIOLATION in lcms.dll+0x9fd2, 86-304 ms, 3/3 runs. Reproduced on two independent Azure VM instances. Windows JDK bundles lcms.dll (not system-linked); Azure WindowsServer:2022-datacenter-azure-edition images ship Temurin 21.0.9 pre-installed.
transicc -l (lcms2's own bundled utility). 4,819-byte device-link profile. SEGV, exit 139.
Python ctypes, Rust lcms2 crate 5.6. Direct calls to cmsCreateTransform with TYPECMYK58. SEGV.
Paths that did not reproduce in my tests: Ghostscript, ImageMagick, tificc, jpgicc, Pillow ImageCms, libvips 8.15, Inkscape, Node.js @kittl/little-cms. See the write-up for per-consumer detail.
Bug (one paragraph) -------------------
src/cmslut.c:461, function CubeSize(). Check-after-multiply on a uint32 accumulator: rv = dim wraps silently before the guard rv > UINTMAX / dim runs. Crafted CLUT dims where the product exceeds 2^32 but wraps to a small value (e.g. [61,7,161,245,255] wraps to 1,529 from a true product of ~4.3e9) pass every guard. cmsStageAllocCLut16bitGranular() undersizes the CLUT buffer (~9 KB instead of ~10 GB of nodes); the interpolator's opta[] strides are computed from the real dims and index past Tab.T[] during transform construction (OptimizeByResampling -> cmsStageSampleCLut16bit) or during cmsDoTransform. CWE-190 causes CWE-125.
Fix status ----------
File: src/cmslut.c Affects: all released versions through lcms2 2.18 Fixed on master (unreleased), no CVE, no advisory: https://github.com/mm2/Little-CMS/commit/da6110b (widen rv to uint64) https://github.com/mm2/Little-CMS/commit/e0641b1 (guard before multiply)
Affected --------
Any distro shipping lcms2 <= 2.18: Ubuntu 24.04 LTS liblcms2-2 2.14-2build1 (validated) Debian bookworm liblcms2-2 2.16-2 Fedora lcms2 2.16 Alpine edge lcms2 2.17-r0 Homebrew little-cms2 2.18 (validated)
JDK-bundled lcms: Temurin 21.0.9 on Windows confirmed vulnerable via its bundled lcms.dll (3/3 runs, EXCEPTIONACCESSVIOLATION in lcms.dll+0x9fd2). On Ubuntu 24.04, OpenJDK 21 uses the SYSTEM liblcms2.so.2, so patching liblcms2-2 fixes both the JDK and Poppler paths on that platform. Other mainstream JDK distributions (Oracle, Corretto, Zulu, Microsoft OpenJDK) commonly bundle their own lcms2 source tree; patch status is per-vendor.
Minimal C reproducer (stock Ubuntu 24.04) -----------------------------------------
sudo apt install liblcms2-dev gcc cat > poc.c <<'EOF' #include <stdio.h> #include <stdlib.h> #include <string.h> #include <lcms2.h> static void be32(unsigned charp,unsigned v){p[0]=v>>24;p[1]=v>>16;p[2]=v>>8;p[3]=v;} int main(void){ const int N = 4587, tag = 32+36+20+N, tot = 128+4+12+tag; unsigned char b = calloc(1,tot); be32(b,tot); b[8]=4; b[9]=0x30; memcpy(b+12,"scnr",4); memcpy(b+16,"5CLR",4); memcpy(b+20,"Lab ",4); memcpy(b+36,"acsp",4); be32(b+68,63190); be32(b+72,65536); be32(b+76,54061); be32(b+128,1); memcpy(b+132,"A2B0",4); be32(b+136,144); be32(b+140,tag); unsigned char t = b+144; memcpy(t,"mAB ",4); t[8]=5; t[9]=3; be32(t+12,32); be32(t+24,68); for (int i=0;i<3;i++) memcpy(t+32+i12,"curv",4); unsigned char g[] = {61,7,161,245,255}; memcpy(t+68,g,5); t[68+16]=1; cmsHPROFILE h = cmsOpenProfileFromMem(b,tot); cmsHPROFILE s = cmsCreatesRGBProfile(); cmsCreateTransform(h, TYPECMYK58, s, TYPERGB8, 0, 0); // SEGV return 0; } EOF # ASAN (clean OOB frame): gcc -fsanitize=address -g -o poc poc.c -llcms2 -lm && ./poc
# Without ASAN (matches production behavior): gcc -o pocplain poc.c -llcms2 -lm && ./pocplain; echo "exit=$?" # exit=139 (SIGSEGV)
Python, Rust, Java, PDF, and device-link variants build equivalently.
CVSS 3.1 --------
Availability only (UI:R): AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H = 6.5 (Medium)
Availability only, server-side renderer (UI:N): AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H = 7.5 (High)
With demonstrated info disclosure, UI:R: AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:H = 8.1 (High)
Same, UI:N (any headless Poppler-backed render worker fits this): AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H = 9.1 (Critical)
I don't have a write primitive. I looked at PatchLUT in cmsopt.c:632 as a possible mirror of the read side, but the math doesn't reach a signed-int wrap.
Information disclosure (CWE-200) --------------------------------
Coarse but real. On Ubuntu 24.04 with glibc's default allocator and ASLR off (setarch -R), the first output byte from cmsDoTransform tracks a pre-run heap-seed byte for specific inputs - so this is a seed-correlated leak of memory below the CLUT allocation. It isn't an arbitrary-heap-read: the first output byte isn't a raw heap byte, it's LinearInterp'd through the sRGB pipeline, so bytes come back with some blur. The reliable window on the 5CLR profile is axis 3's [-365 KB, -1.5 KB] offsets below the CLUT allocation.
Two small tricks in cmsintrp.c make this work:
EVALFNS(N,NM) short-circuits the far-corner read when Input[i] == 0xFFFFU. With 8-bit input that's byte 0xFF, so setting 4 of 5 axes to 0xFF collapses the usual 2^5=32 corner reads down to 2.
opta[NM] is uint32; opta[NM] k0 is computed as uint32 and wraps mod 2^32. The wrapped value then goes into int K0, and anything above 2^31 reinterprets as a large negative int. So LutTable + K0 ends up reading below the CLUT allocation, in heap we've just sprayed.
Axis 3 (opta[1] = 765) gives offsets of -1.5 KB to -365 KB, which a 260 MB malloc-spray covers comfortably.
Evidence (16 sampled seed bytes spanning 0x00..0xFF: 0x00, 0x11, 0x22, ..., 0xEE, 0xFF):
seed=0xAA axis=3 in=0xd9 out=aa3b53 (byte[0] = seed) seed=0xAA axis=3 in=0xf5 out=add800 (byte[0] ~ seed) seed=0xCC axis=3 in=0xd9 out=e32b45 (byte[0] tracks seed) seed=0xCC axis=3 in=0xf5 out=ebe300 (byte[0] tracks seed)
seed=0xAA axis=3 in=0xea out=005f91 (control, in-bounds) seed=0xCC axis=3 in=0xea out=005f91 (same)
Control input (0xea, in-bounds) produces byte-identical output across all 16 seeds; OOB inputs (0xd9, 0xf4, 0xf5) produce outputs whose first byte tracks the heap seed.
POC (infoleaklinuxv3.c) and the 16-seed-sweep logs on request. Caveats: ASLR must be off and glibc's default allocator is assumed. Axis 3 is the reliable surface; axes 0-2 fall too far out of bounds without MAPFIXED reservations or multi-GB sprays. The primitive is seed-correlated, not arbitrary-read.
Timeline --------
2010-10 CubeSize() check-after-multiply pattern introduced. 2026-02-19 Fix 1: da6110b. 2026-03-12 Fix 2: e0641b1. 2026-04-13 GHSA-4xp6-rcgg-m9qq filed (private advisory). 2026-04-14 MITRE CVE request filed (CVE Request 2025002). Submitted with the evidence that existed at the time. 2026-04-16 Asked the maintainer on the GHSA whether he'd triage, told him I'd publish otherwise. 2026-04-17 GHSA closed without engagement. Public disclosure
References ----------
Vulnerable source (lcms2 2.18): https://github.com/mm2/Little-CMS/blob/lcms2.18/src/cmslut.c#L461 Prior same-codebase CVEs: CVE-2018-16435, CVE-2016-10165. CWEs: CWE-190, CWE-125. Write-up + per-consumer evidence: https://abhinavagarwal07.github.io/posts/lcms2-cubesize-overflow/
-- Abhinav Agarwal
Little CMS (lcms2) through 2.18 has an integer overflow in CubeSize in cmslut.c because the overflow check is performed after the multiplication.
Last updated 2 June 2026
A 992-byte PDF crashes a bunch of stock Ubuntu 24.04 consumers: evince-thumbnailer, Poppler (pdftoppm / pdftocairo / pdfimages), the cups-filters PDF-to-raster print filter, Okular, and GIMP's PDF plug-in all segfault inside liblcms2. OpenJDK 21 on Ubuntu crashes too, and Windows Temurin 21.0.9 crashes in its bundled lcms.dll (3/3 independent runs). There's also a coarse seed- correlated heap-read primitive on Linux glibc with ASLR off - a real CWE-200 channel, though not a generic arbitrary read. Upstream fixed it on master in February/March but hasn't cut a release, no advisory, no CVE. The GHSA I filed was closed without a reply. Looking for a CVE and for distro attention.
Full write-up: https://abhinavagarwal07.github.io/posts/lcms2-cubesize-overflow/
Reachability (Ubuntu 24.04 LTS stock, liblcms2-2 2.14-2build1; Windows Server 2022 with Temurin 21.0.9) --------------------------------------------------------------------------
SEGV, no local code changes:
tumblerd (D-Bus auto-activated thumbnail service). tumblerd is the freedesktop thumbnail daemon that ships as the default on Xfce and is available on GNOME as a fallback; its bundled tumbler-poppler-thumbnailer.so plugin loads libpoppler + liblcms2 directly into the daemon process. A single dbus-send "Queue" call with the PDF's URI is enough: tumblerd was not running beforehand and wasn't on $PATH, but D-Bus auto-activated the service on the Queue call, the service pulled the PDF, and the daemon SIGSEGV'd in liblcms2.so.2.0.14. Reproduced 4/4, with kernel segfault ... in liblcms2.so.2.0.14 and apport records. This is the same D-Bus call that a file manager issues when a directory is opened, so the real-world shape is "open a folder containing the PDF, the system's thumbnail daemon dies."
Direct evince-thumbnailer CLI (evince-thumbnailer -s 200 poc.pdf out.jpg) crashes the same way (SEGV at liblcms2.so.2.0.14+0xb503, Eval4Inputs+643, cmsintrp.c:909).
SHA256 (pociccbased5ch.pdf): 5c328a4362185c6dca2d6cae13c74ed456889798220f3f16e840449648121b55
Poppler: pdftoppm, pdftocairo, pdfimages -list. Same 992-byte PDF with a 1x1 image XObject using /ColorSpace [/ICCBased 5 0 R]. Poppler warns on N>4 and does not abort; goes on to call cmsCreateTransform(). Same crash site.
Okular 4:23.08.5 (xvfb-run). SEGV via okularGeneratorpoppler.so -> libpoppler-qt5 -> lcms2. Kernel: Okular::PixmapG[PID]: segfault ... in liblcms2.so.2.0.14[0xb503], Eval4Inputs+643. Core file + gdb backtrace captured.
cups-filters pdftoraster 2.0.0-0ubuntu4.1. This is the CUPS PDF-to-raster filter. It lives at /usr/lib/cups/filter/pdftoraster rather than on $PATH, so which pdftoraster misses it - invoke it the way CUPS does: /usr/lib/cups/filter/pdftoraster 1 root "" 1 "" < poc.pdf. Kernel: pdftoraster[PID]: segfault ... in liblcms2.so.2.0.14[0xb503]. Core + gdb backtrace captured in the primary-evidence bundle.
GIMP 2.10.36-3 file-pdf-load plug-in (under xvfb-run, headless batch mode). The plug-in subprocess SIGSEGVs. GIMP installs its own signal handler, so the usual kernel dmesg line doesn't appear, but strace catches SIGSEGV{sicode=SEGVACCERR} at fault time, and the frame-by-frame proof comes from running the same PDF through evince-thumbnailer under gdb - identical poppler + lcms2 library chain.
LibreOffice import: inconsistent enough that I'd treat it as a secondary target rather than cite it as confirmed. On the authoritative fresh-VM run under script(1), LO rejected the PDF at the load stage before ever calling into lcms2. On a separate VM earlier, xpdfimport crashed with a matching dmesg line. Both outcomes reproduce; I can't point at a single reliable command that crashes LO the way the other rows do.
Flask+Docker PDF thumbnailer spawning pdftoppm returns HTTP 500 (exitcode:-11) per upload. Same shape as any Poppler-backed webmail preview, DMS thumbnailer, or CI artifact renderer.
OpenJDK 21 on Ubuntu. ICCProfile.getInstance() + ICCColorSpace.toRGB(). SEGV in system liblcms2.so.2. Confirmed with both the 18 MB 7CLR profile and a 4,819-byte 5CLR variant (JdkPoc5.java, input array sized via getNumComponents()).
OpenJDK 21 Temurin 21.0.9 on Windows Server 2022. EXCEPTIONACCESSVIOLATION in lcms.dll+0x9fd2, 86-304 ms, 3/3 runs. Reproduced on two independent Azure VM instances. Windows JDK bundles lcms.dll (not system-linked); Azure WindowsServer:2022-datacenter-azure-edition images ship Temurin 21.0.9 pre-installed.
transicc -l (lcms2's own bundled utility). 4,819-byte device-link profile. SEGV, exit 139.
Python ctypes, Rust lcms2 crate 5.6. Direct calls to cmsCreateTransform with TYPECMYK58. SEGV.
Paths that did not reproduce in my tests: Ghostscript, ImageMagick, tificc, jpgicc, Pillow ImageCms, libvips 8.15, Inkscape, Node.js @kittl/little-cms. See the write-up for per-consumer detail.
Bug (one paragraph) -------------------
src/cmslut.c:461, function CubeSize(). Check-after-multiply on a uint32 accumulator: rv = dim wraps silently before the guard rv > UINTMAX / dim runs. Crafted CLUT dims where the product exceeds 2^32 but wraps to a small value (e.g. [61,7,161,245,255] wraps to 1,529 from a true product of ~4.3e9) pass every guard. cmsStageAllocCLut16bitGranular() undersizes the CLUT buffer (~9 KB instead of ~10 GB of nodes); the interpolator's opta[] strides are computed from the real dims and index past Tab.T[] during transform construction (OptimizeByResampling -> cmsStageSampleCLut16bit) or during cmsDoTransform. CWE-190 causes CWE-125.
Fix status ----------
File: src/cmslut.c Affects: all released versions through lcms2 2.18 Fixed on master (unreleased), no CVE, no advisory: https://github.com/mm2/Little-CMS/commit/da6110b (widen rv to uint64) https://github.com/mm2/Little-CMS/commit/e0641b1 (guard before multiply)
Affected --------
Any distro shipping lcms2 <= 2.18: Ubuntu 24.04 LTS liblcms2-2 2.14-2build1 (validated) Debian bookworm liblcms2-2 2.16-2 Fedora lcms2 2.16 Alpine edge lcms2 2.17-r0 Homebrew little-cms2 2.18 (validated)
JDK-bundled lcms: Temurin 21.0.9 on Windows confirmed vulnerable via its bundled lcms.dll (3/3 runs, EXCEPTIONACCESSVIOLATION in lcms.dll+0x9fd2). On Ubuntu 24.04, OpenJDK 21 uses the SYSTEM liblcms2.so.2, so patching liblcms2-2 fixes both the JDK and Poppler paths on that platform. Other mainstream JDK distributions (Oracle, Corretto, Zulu, Microsoft OpenJDK) commonly bundle their own lcms2 source tree; patch status is per-vendor.
Minimal C reproducer (stock Ubuntu 24.04) -----------------------------------------
sudo apt install liblcms2-dev gcc cat > poc.c <<'EOF' #include <stdio.h> #include <stdlib.h> #include <string.h> #include <lcms2.h> static void be32(unsigned charp,unsigned v){p[0]=v>>24;p[1]=v>>16;p[2]=v>>8;p[3]=v;} int main(void){ const int N = 4587, tag = 32+36+20+N, tot = 128+4+12+tag; unsigned char b = calloc(1,tot); be32(b,tot); b[8]=4; b[9]=0x30; memcpy(b+12,"scnr",4); memcpy(b+16,"5CLR",4); memcpy(b+20,"Lab ",4); memcpy(b+36,"acsp",4); be32(b+68,63190); be32(b+72,65536); be32(b+76,54061); be32(b+128,1); memcpy(b+132,"A2B0",4); be32(b+136,144); be32(b+140,tag); unsigned char t = b+144; memcpy(t,"mAB ",4); t[8]=5; t[9]=3; be32(t+12,32); be32(t+24,68); for (int i=0;i<3;i++) memcpy(t+32+i12,"curv",4); unsigned char g[] = {61,7,161,245,255}; memcpy(t+68,g,5); t[68+16]=1; cmsHPROFILE h = cmsOpenProfileFromMem(b,tot); cmsHPROFILE s = cmsCreatesRGBProfile(); cmsCreateTransform(h, TYPECMYK58, s, TYPERGB8, 0, 0); // SEGV return 0; } EOF # ASAN (clean OOB frame): gcc -fsanitize=address -g -o poc poc.c -llcms2 -lm && ./poc
# Without ASAN (matches production behavior): gcc -o pocplain poc.c -llcms2 -lm && ./pocplain; echo "exit=$?" # exit=139 (SIGSEGV)
Python, Rust, Java, PDF, and device-link variants build equivalently.
CVSS 3.1 --------
Availability only (UI:R): AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H = 6.5 (Medium)
Availability only, server-side renderer (UI:N): AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H = 7.5 (High)
With demonstrated info disclosure, UI:R: AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:H = 8.1 (High)
Same, UI:N (any headless Poppler-backed render worker fits this): AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H = 9.1 (Critical)
I don't have a write primitive. I looked at PatchLUT in cmsopt.c:632 as a possible mirror of the read side, but the math doesn't reach a signed-int wrap.
Information disclosure (CWE-200) --------------------------------
Coarse but real. On Ubuntu 24.04 with glibc's default allocator and ASLR off (setarch -R), the first output byte from cmsDoTransform tracks a pre-run heap-seed byte for specific inputs - so this is a seed-correlated leak of memory below the CLUT allocation. It isn't an arbitrary-heap-read: the first output byte isn't a raw heap byte, it's LinearInterp'd through the sRGB pipeline, so bytes come back with some blur. The reliable window on the 5CLR profile is axis 3's [-365 KB, -1.5 KB] offsets below the CLUT allocation.
Two small tricks in cmsintrp.c make this work:
EVALFNS(N,NM) short-circuits the far-corner read when Input[i] == 0xFFFFU. With 8-bit input that's byte 0xFF, so setting 4 of 5 axes to 0xFF collapses the usual 2^5=32 corner reads down to 2.
opta[NM] is uint32; opta[NM] k0 is computed as uint32 and wraps mod 2^32. The wrapped value then goes into int K0, and anything above 2^31 reinterprets as a large negative int. So LutTable + K0 ends up reading below the CLUT allocation, in heap we've just sprayed.
Axis 3 (opta[1] = 765) gives offsets of -1.5 KB to -365 KB, which a 260 MB malloc-spray covers comfortably.
Evidence (16 sampled seed bytes spanning 0x00..0xFF: 0x00, 0x11, 0x22, ..., 0xEE, 0xFF):
seed=0xAA axis=3 in=0xd9 out=aa3b53 (byte[0] = seed) seed=0xAA axis=3 in=0xf5 out=add800 (byte[0] ~ seed) seed=0xCC axis=3 in=0xd9 out=e32b45 (byte[0] tracks seed) seed=0xCC axis=3 in=0xf5 out=ebe300 (byte[0] tracks seed)
seed=0xAA axis=3 in=0xea out=005f91 (control, in-bounds) seed=0xCC axis=3 in=0xea out=005f91 (same)
Control input (0xea, in-bounds) produces byte-identical output across all 16 seeds; OOB inputs (0xd9, 0xf4, 0xf5) produce outputs whose first byte tracks the heap seed.
POC (infoleaklinuxv3.c) and the 16-seed-sweep logs on request. Caveats: ASLR must be off and glibc's default allocator is assumed. Axis 3 is the reliable surface; axes 0-2 fall too far out of bounds without MAPFIXED reservations or multi-GB sprays. The primitive is seed-correlated, not arbitrary-read.
Timeline --------
2010-10 CubeSize() check-after-multiply pattern introduced. 2026-02-19 Fix 1: da6110b. 2026-03-12 Fix 2: e0641b1. 2026-04-13 GHSA-4xp6-rcgg-m9qq filed (private advisory). 2026-04-14 MITRE CVE request filed (CVE Request 2025002). Submitted with the evidence that existed at the time. 2026-04-16 Asked the maintainer on the GHSA whether he'd triage, told him I'd publish otherwise. 2026-04-17 GHSA closed without engagement. Public disclosure
References ----------
Vulnerable source (lcms2 2.18): https://github.com/mm2/Little-CMS/blob/lcms2.18/src/cmslut.c#L461 Prior same-codebase CVEs: CVE-2018-16435, CVE-2016-10165. CWEs: CWE-190, CWE-125. Write-up + per-consumer evidence: https://abhinavagarwal07.github.io/posts/lcms2-cubesize-overflow/
-- Abhinav Agarwal
A heap buffer overflow vulnerability has been identified in the lcms2-2.16. The vulnerability exists in the UnrollChunkyBytes function in cmspack.c, which is responsible for handling color space transformations. NOTE: this is disputed by the Supplier because the finding identified a bug in a third-party calling program, not in lcms.
A heap buffer overflow vulnerability has been identified in thesmooth2() in cmsgamma.c in lcms2-2.16 which allows a remote attacker to cause a denial of service. NOTE: the Supplier disputes this because "this is not exploitable as this function is never called on normal color management, is there only as a helper for low-level programming and investigation."
Ibrahim El-Sayed discovered that Little CMS incorrectly handled certain files. An attacker could possibly use this issue to cause a denial of service. (CVE-2016-10165) Quang Nguyen discovered that Little CMS incorrectly handled certain files. An attacker could possibly use this issue to execute arbitrary code. (CVE-2018-16435)
A flaw was found in Little CMS (aka Little Color Management System) 2.9. An integer overflow in the AllocateDataSet function in cmscgats.c, leading to a heap-based buffer overflow in the SetData function via a crafted file in the second argument to cmsIT8LoadFromFile.
References: https://github.com/mm2/Little-CMS/issues/171
Upstream Fix: https://github.com/mm2/Little-CMS/commit/768f70ca405cd3159d990e962d54456773bb8cf8
An out-of-bounds read in cmstypes.c in TypeMLURead function was found, leading to heap memory leak triggered by crafted ICC profile.
Upstream patch:
https://github.com/mm2/Little-CMS/commit/5ca71a7bc18b6897ab21d815d15e218e204581e2
CVE request:
http://seclists.org/oss-sec/2016/q3/288
SQL injection vulnerability in index.php in CMS little 0.0.1 allows remote attackers to execute arbitrary SQL commands via the term parameter.