fix(#1982): honour the D2B/B2D opt-out for dual-PCS profiles - #2292
Conversation
PR Note2026-08-24 15:28:42 UTC
|
CIccXform::Create selected a spectral DToBx/BToDx transform whenever the profile carried a spectralPCS, ignoring the caller's bUseD2BTags opt-out. Every other space-selection site already tests !pcs as well: CIccCmm::AddXform at IccCmm.cpp:9093/:9112 and CIccNamedColorCmm::AddXform at :11718/:11796/:11813 all read (bUseD2BxB2DxTags || !m_Header.pcs). So for a profile carrying both a colorimetric and a spectral PCS the sites disagreed - AddXform recorded the 3-sample XYZ connection while Create returned a transform emitting 36 spectral samples, and CIccCmm::Begin() rejected the chain outright. Both Create lines were introduced together in 92ffea9 (#1018, hybrid spectral profiles); the AddXform half already carried the !pcs term and the Create half did not. With this change all seven sites agree. Measured on master f78e915, Clang 21.1.3 Release, with SixChanInputRef (AToB3 6->3, DToB3 6->36): iccApplyToLink link.icc 0 2 1 T 0 1 1 0 SixChanInputRef.icc 11 before: exit 255, "status 2: Invalid space link" after: exit 0, XYZ PCS, "MPE Element Chain: 1 elements, 6->3 channels" Same for intent 13. Both intents decode to icXformLutColor with bUseD2BxB2DxTags cleared, i.e. an explicit opt-out. The output half has to land in the same commit. The two mis-selections currently cancel out: a chain whose source and destination are both dual-PCS resolves today because both ends silently take the spectral route. With only the input site corrected, SixChanInputRef into SixChanCameraRef under intent 11 regresses from exit 0 to "status 13: Unsupported PCS Link used". #1018 is not regressed. CMYK_Hybrid_Profile's spectral sub-profile has an empty PCS, so !pcs holds and its DToBx path stays open; iccdev.hybrid-pipeline passes. New CTest iccdev.issue-1982-dual-pcs-selection-regression covers both directions. Part 1 asserts exit status and link shape for the source profile. Part 2 has to be relative: the destination-side chain builds a link either way, just the wrong one, so it generates the chain with both ends opting out (11,11) and both opting in (1,1) and requires the two links to differ. On master the two are byte-identical once the volatile header fields are stripped. It exits 77 with SKIP_RETURN_CODE when the tools are absent, so a build that cannot run it reports Skipped rather than a green pass. No pre-existing test exercised the opt-out path at all - the only other script using a tens-digit-1 intent code is the NaN PoC, which fails at range parsing before AddXform - so a green suite is not the evidence here; the three-way red/green is: master f78e915 -> FAIL (part 1) input-site fix only -> FAIL (part 2) both sites fixed -> PASS Pre-flight on f37dbb3: Clang 21.1.3 Release, -Wall -Wextra -Wpedantic -Werror, strict warnings ENABLED: 0 warnings, ctest 223/224 GCC 15.2.0 in ghcr.io/internationalcolorconsortium/iccdev-ci-regression :latest, same flags + LTO, strict warnings ENABLED: 0 warnings, build and build-test-binaries both rc=0 Clang ASan+UBSan Debug: ctest 222/223, new test passes in 0.34s detect_leaks=1 over the six affected iccApplyToLink paths, including the newly reachable ProfileMissingTag refusal: no leaks, no findings The one failure in each ctest run is iccdev.spectral-tiff-preview, which needs the imagecodecs Python module and fails identically on pristine master.
179bc6e to
8aeb407
Compare
There was a problem hiding this comment.
Pull request overview
Fixes dual-PCS transform selection to honor D2B/B2D opt-outs and adds regression coverage.
Changes:
- Updates source and destination transform selection.
- Registers the issue-1982 CTest regression.
- Adds two-direction route validation.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
IccProfLib/IccCmm.cpp |
Honors D2B/B2D opt-outs for dual-PCS profiles. |
Build/Cmake/Testing/CMakeLists.txt |
Registers the regression test. |
.github/scripts/iccdev-issue-1982-dual-pcs-selection-regression.sh |
Validates colorimetric and spectral route selection. |
Suppressed comments (1)
.github/scripts/iccdev-issue-1982-dual-pcs-selection-regression.sh:135
- The CTest harness sets
ASAN_OPTIONS/UBSAN_OPTIONSwithhalt_on_error=0, so a sanitizer finding during thisiccFromXmlinvocation can be printed while the command still returns zero. Unlike the latericcApplyToLinkcalls, this log is never passed tosanitizer_check(and the same gap exists for the dump logs), so the new ASAN-labeled regression can pass with a sanitizer finding in its setup/assertion tools. Check each generated-tool log before relying on its assertions.
"$from_xml" "$src_xml" "$src_icc" >"$outdir/fromxml-input.log" 2>&1 ||
fail "iccFromXml failed on $src_xml; see $outdir/fromxml-input.log"
"$dump_profile" -v 100 "$src_icc" ALL >"$outdir/input.log" 2>&1
Pre Merge Report2026-08-24 15:40:48 UTC TODO as Punch List following Merge
|
Summary
Harvests the
CIccXform::Createhalf ofci-qa-issue-1982(ea41af37, @xsscx) and addsa two-direction regression CTest. Part of #1982 — items 1 and 2; item 3 is already
implemented, and one behaviour question is left open for a maintainer (both below).
Deliberately not a closing keyword.
CIccXform::Createselected a spectralDToBx/BToDxtransform whenever the profilecarried a
spectralPCS, ignoring the caller'sbUseD2BTagsopt-out. Every otherspace-selection site already tests
!pcsas well —CIccCmm::AddXformatIccCmm.cpp:9093/:9112andCIccNamedColorCmm::AddXformat:11718/:11796/:11813all read
(bUseD2BxB2DxTags || !m_Header.pcs). For a profile carrying both a colorimetricand a spectral PCS the sites disagreed:
AddXformrecorded the 3-sample XYZ connectionwhile
Createreturned a transform emitting 36 spectral samples, andCIccCmm::Begin()rejected the chain.
Both
Createlines were introduced together in92ffea98(#1018, hybrid spectralprofiles). The
AddXformhalf already carried the!pcsterm; the twoCreatesites didnot. With this change all seven sites agree.
Measured
Clang 21.1.3 Release, master
f78e915a,Testing/SpecRef/SixChanInputRef.xml(
AToB36→3,DToB36→36,PCS=XYZ,SpectralPCS=rs0024):Same for intent 13. Both decode to
icXformLutColorwithbUseD2BxB2DxTagscleared.Instrumented master, whole 222-test suite, one probe per decision site:
Why both sites in one commit
The two mis-selections cancel out. A chain whose source and destination are dual-PCS
resolves today because both ends silently take the spectral route. With only the input
site corrected:
#1018 is not regressed
CMYK_Hybrid_Profile's spectral sub-profile has an empty<PCS>, so!pcsholds and itsDToBxpath stays open.iccdev.hybrid-pipelinepasses: 43s Release, 494s ASan+UBSan.The test
iccdev.issue-1982-dual-pcs-selection-regression, both directions.Part 1 (source) asserts exit status and link shape. Part 2 (destination) cannot: the
chain builds a link either way, just the wrong one, so an exit-status check would be
vacuous. It builds the same chain twice — both ends opting out
(11,11)and both optingin
(1,1)— and requires the two links to differ. On master they are byte-identical onceCreation Date/Profile ID/ size / banner are stripped. Both arms succeed in bothbuilds, so this asserts the route and not an error.
Red/green:
f78e915aNo pre-existing test exercised the opt-out path at all. The only other script using a
tens-digit-1 intent code is
iccdev-applytolink-nan-sixchan-debug.sh, and it feedsnanas the range so it fails before
AddXform. A green suite is therefore not the evidencehere — the three-way red/green above is. The test exits 77 with
SKIP_RETURN_CODEwhenthe tools are absent, so a build that cannot run it reports
Skippedrather than a greenpass.
Pre-flight
Base
d1a7d42a. Dispatchedci-pr-actionwithci_scope=sourcebefore opening this PRper the suggested workflow: run
32743042807,
success, 12 jobs green / 3 skipped by scope — GCC 15.2 Strict Release LTO, Windows
MSVC + ClangCL, MinGW UCRT64, macOS Release + Debug, Tool Smoke Tests ASAN+UBSAN. On the
ASAN leg the new test ran as
149/220 Test #150 ... Passed 0.54 sec(100% tests passed, 0 failed out of 220); script CTests are not registered on the Windows leg, whichnonetheless configured the new registration under MSVC (129 tests, 100%).
Local lanes:
-Wall -Wextra -Wpedantic -Werror, strict warnings ENABLEDghcr.io/internationalcolorconsortium/iccdev-ci-regression:latest, same flags + LTO, strict warnings ENABLEDbuild-test-binariesboth rc=0detect_leaks=1over the six affectediccApplyToLinkpaths, including the newly reachableProfileMissingTagrefusalshellcheck0.9.0The one ctest failure in each run is
iccdev.spectral-tiff-preview, which needs theimagecodecsPython module and fails identically on pristine master.One behaviour change worth a maintainer's eye
Testing/Calc/CameraModel.xmlis dual-PCS withA2B3+B2D3and noB2A3. As adestination under the opt-out it now returns
icCmmStatProfileMissingTagwhere mastersilently used the spectral
B2D3. A sweep of all 221 tracked XML found only threedual-PCS profiles carrying
B2Dtags, and CameraModel is used only for XML→ICC, dump andwriter-failure tests — never as a CMM destination — so nothing in the suite covers it
either way. Whether an opt-out should fall back to the spectral tag when no colorimetric
tag exists in that direction is a spec call, raised on #1982 rather than decided here.
Not harvested: the branch's third change
ci-qa-issue-1982also adds a sample-count guard inCIccCmm::AddXform(issue item 3).Left out, measured against the branch applied verbatim:
CIccCmm::Begin()already does it. Its twoGetNumSrcSamples()/GetNumDstSamples()comparisons against the CMM's counts carry the comment "Otherwise we'll have a heap
overflow during Apply." That is exactly why the unfixed dual-PCS case fails closed with
icCmmStatBadSpaceLinkrather than overflowing.It fires twice in the 222-test suite and both tests still print PASS.
iccdev.issue-1336-applytolink-pcc-regressionfuzz-abst-a044ff69.icc, status 2)fuzz-mntr-ef352586.icc, status 4)iccdev.issue-1985-encoding-lumamtx-uafCI c0d0fc2: Indirect Leak in CIccProfile::ReadBasic() at IccProfile.cpp:1363 #1336 exists to prove a
-PCCprofile is released on theAddXformerror path. Movingthe failure to the first profile means that path is never reached, so the test goes
vacuous while still reporting
[PASS]— it only greps for a LeakSanitizer report. CIccDefaultEncProfileConverter::ConvertFromParams frees a borrowed colorEncodingParams element — heap use-after-free write, then double free (IccEncoding.cpp:259) #1985'sown header documents the expected outcome as "the ordinary 'invalid space link'
rejection", which the guard changes.
It is not a channel-count check in practice.
icGetSpaceSamples()returns0for anysignature it does not map, so the guard is really "reject any profile whose PCS signature
is unrecognised or zero". Both firings were
.../0cases —nDstSpace=00000000, andnSrcSpace=F6BD2F3Efrom the fuzzed abstract profile — not genuine disagreements.Reproduce: apply the guard and run
ctest -R 'issue-1336|issue-1985'.Checklist
docs/build.mddocs/ctest.mdm_members