From 9166e3f98b290cc451cc3ff675e91b25b324907a Mon Sep 17 00:00:00 2001 From: 94xhn <87560781+94xhn@users.noreply.github.com> Date: Sat, 18 Jul 2026 15:34:01 +0800 Subject: [PATCH] Fix memory leak of shaper buffer in ParseCube() when loading .cube files ParseCube() allocates a temporary 'shapers' buffer to stage the LUT_1D_SIZE tone-curve samples before handing them to cmsBuildTabulatedToneCurveFloat() and cmsStageAllocToneCurves(). Both of those copy the sample data into their own storage (AllocateToneCurveStruct() duplicates the segment's SampledPoints via _cmsDupMem, and cmsStageAllocToneCurves() duplicates each tone curve via cmsDupToneCurve), so 'shapers' is never referenced again after cmsFreeToneCurveTriple(curves) runs -- but it was never freed either. This leaks 3 * shaper_size * sizeof(cmsFloat32Number) bytes on every single successful load of a .cube file that carries a LUT_1D_SIZE (1D shaper) entry, e.g. via cmsCreateDeviceLinkFromCubeFile(THR)(). The sibling 3D CLUT path a few lines below already frees its equivalent scratch buffer (_cmsFree(cube->ContextID, lut_table)); this brings the shaper path in line with it. Verified independently: built the library from this source with -fsanitize=address,undefined and reproduced the leak with a minimal LUT_1D_SIZE-only .cube file loaded via cmsCreateDeviceLinkFromCubeFile(); LeakSanitizer reported exactly the 'shapers' allocation at cmscgats.c (48 bytes for shaper_size=4). After adding the _cmsFree() call the same repro is clean (no leak, no use-after-free/heap-corruption from freeing the buffer after its data has been copied out). Also ran the existing testbed/testcms2.c regression suite before/after with no behavioral change. --- src/cmscgats.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cmscgats.c b/src/cmscgats.c index b2331b44..82fb2b59 100644 --- a/src/cmscgats.c +++ b/src/cmscgats.c @@ -3187,6 +3187,7 @@ cmsBool ParseCube(cmsIT8* cube, cmsStage** Shaper, cmsStage** CLUT, char title[] *Shaper = cmsStageAllocToneCurves(cube->ContextID, 3, curves); cmsFreeToneCurveTriple(curves); + _cmsFree(cube->ContextID, shapers); } if (lut_size > 0) {