From 3464f489f7b7178ab048cd9df35d62b93a2b7864 Mon Sep 17 00:00:00 2001 From: viktoriia-lsg <90571271+viktoriia-lsg@users.noreply.github.com> Date: Mon, 6 Nov 2023 16:06:24 +0200 Subject: [PATCH] Fuzzing Coverage Expansion of Little-CMS (#11165) Hi! This pull request expands fuzzing coverage of Little-CMS by adding 7 new fuzz targets and modifying 1 existing fuzz target (cms_transform_extended_fuzzer). Docker file includes 7 new ICC profiles derived from Little-CMS/testbed and build.sh is modified and added with new fuzz targets and their seed corpuses. --- projects/lcms/Dockerfile | 8 ++ projects/lcms/build.sh | 25 ++++- projects/lcms/cms_cgats_fuzzer.c | 76 +++++++++++++++ projects/lcms/cms_cie_cam02_fuzzer.c | 51 ++++++++++ projects/lcms/cms_devicelink_fuzzer.c | 32 +++++++ projects/lcms/cms_dict_fuzzer.c | 95 +++++++++++++++++++ projects/lcms/cms_gdb_fuzzer.c | 43 +++++++++ projects/lcms/cms_md5_fuzzer.c | 26 +++++ projects/lcms/cms_postscript_fuzzer.c | 44 +++++++++ projects/lcms/cms_transform_extended_fuzzer.c | 16 +++- projects/lcms/cms_virtual_profile_fuzzer.c | 88 +++++++++++++++++ 11 files changed, 499 insertions(+), 5 deletions(-) create mode 100644 projects/lcms/cms_cgats_fuzzer.c create mode 100644 projects/lcms/cms_cie_cam02_fuzzer.c create mode 100644 projects/lcms/cms_devicelink_fuzzer.c create mode 100644 projects/lcms/cms_dict_fuzzer.c create mode 100644 projects/lcms/cms_gdb_fuzzer.c create mode 100644 projects/lcms/cms_md5_fuzzer.c create mode 100644 projects/lcms/cms_postscript_fuzzer.c create mode 100644 projects/lcms/cms_virtual_profile_fuzzer.c diff --git a/projects/lcms/Dockerfile b/projects/lcms/Dockerfile index aaa551794..8d44385cf 100644 --- a/projects/lcms/Dockerfile +++ b/projects/lcms/Dockerfile @@ -24,6 +24,14 @@ RUN mkdir $SRC/seeds && \ cp $SRC/lcms/testbed/test1.icc . && \ cp $SRC/lcms/testbed/crayons.icc . && \ cp $SRC/lcms/testbed/ibm-t61.icc . && \ + #add more seeds from the testbed dir + cp $SRC/lcms/testbed/bad_mpe.icc . && \ + cp $SRC/lcms/testbed/new.icc . && \ + cp $SRC/lcms/testbed/test2.icc . && \ + cp $SRC/lcms/testbed/test3.icc . && \ + cp $SRC/lcms/testbed/test4.icc . && \ + cp $SRC/lcms/testbed/test5.icc . && \ + cp $SRC/lcms/testbed/TestCLT.icc . && \ zip -rj $SRC/seed_corpus.zip $SRC/seeds/* WORKDIR lcms diff --git a/projects/lcms/build.sh b/projects/lcms/build.sh index f58fe39a4..1f9e949a8 100755 --- a/projects/lcms/build.sh +++ b/projects/lcms/build.sh @@ -26,7 +26,16 @@ FUZZERS="cmsIT8_load_fuzzer \ cms_transform_all_fuzzer \ cms_profile_fuzzer \ cms_universal_transform_fuzzer \ - cms_transform_extended_fuzzer" + cms_transform_extended_fuzzer \ + cms_md5_fuzzer \ + cms_dict_fuzzer \ + cms_postscript_fuzzer \ + cms_cie_cam02_fuzzer \ + cms_gdb_fuzzer \ + cms_cgats_fuzzer \ + cms_virtual_profile_fuzzer \ + cms_devicelink_fuzzer" + for F in $FUZZERS; do $CC $CFLAGS -c -Iinclude \ @@ -36,11 +45,21 @@ for F in $FUZZERS; do $LIB_FUZZING_ENGINE src/.libs/liblcms2.a done -cp $SRC/icc.dict $SRC/*.options $OUT/ +cp $SRC/*.dict $SRC/*.options $OUT/ cp $SRC/icc.dict $OUT/cms_transform_all_fuzzer.dict cp $SRC/icc.dict $OUT/cms_transform_extended_fuzzer.dict cp $SRC/icc.dict $OUT/cms_universal_transform_fuzzer.dict cp $SRC/icc.dict $OUT/cms_profile_fuzzer.dict -cp $SRC/seed_corpus.zip $OUT/cms_transform_fuzzer_seed_corpus.zip +cp $SRC/icc.dict $OUT/cms_postscript_fuzzer.dict +cp $SRC/icc.dict $OUT/cms_virtual_profile_fuzzer.dict +cp $SRC/icc.dict $OUT/cms_md5_fuzzer.dict +cp $SRC/seed_corpus.zip $OUT/cms_postscript_fuzzer_seed_corpus.zip cp $SRC/seed_corpus.zip $OUT/cms_profile_fuzzer_seed_corpus.zip cp $SRC/seed_corpus.zip $OUT/cms_universal_transform_fuzzer_seed_corpus.zip +cp $SRC/seed_corpus.zip $OUT/cms_transform_all_fuzzer_seed_corpus.zip +cp $SRC/seed_corpus.zip $OUT/cms_transform_extended_fuzzer_seed_corpus.zip +cp $SRC/seed_corpus.zip $OUT/cms_transform_fuzzer_seed_corpus.zip +cp $SRC/seed_corpus.zip $OUT/cms_virtual_profile_fuzzer_seed_corpus.zip +cp $SRC/seed_corpus.zip $OUT/cmsIT8_load_fuzzer_seed_corpus.zip +cp $SRC/seed_corpus.zip $OUT/cms_md5_fuzzer_seed_corpus.zip +cp $SRC/seed_corpus.zip $OUT/cms_overwrite_transform_fuzzer_seed_corpus.zip \ No newline at end of file diff --git a/projects/lcms/cms_cgats_fuzzer.c b/projects/lcms/cms_cgats_fuzzer.c new file mode 100644 index 000000000..627ac1361 --- /dev/null +++ b/projects/lcms/cms_cgats_fuzzer.c @@ -0,0 +1,76 @@ +/* Copyright 2023 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include "lcms2.h" + + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + if (size < 8){ + return 0; + } + + cmsContext context = cmsCreateContext(NULL, (void *)data); + + uint32_t Row = *((uint32_t *)data); + uint32_t Col = *((uint32_t *)data+1); + + /* Write */ + cmsHANDLE it8; + cmsInt32Number i; + + it8 = cmsIT8Alloc(0); + if (it8 == NULL) return 0; + + cmsIT8SetSheetType(it8, "LCMS/TESTING"); + cmsIT8SetPropertyStr(it8, "ORIGINATOR", "1 2 3 4"); + cmsIT8SetPropertyUncooked(it8, "DESCRIPTOR", "1234"); + cmsIT8SetPropertyStr(it8, "MANUFACTURER", "3"); + cmsIT8SetPropertyDbl(it8, "CREATED", data[0] / 255.0); + cmsIT8SetPropertyDbl(it8, "SERIAL", data[1] / 255.0); + cmsIT8SetPropertyHex(it8, "MATERIAL", 0x123); + + cmsIT8SetPropertyDbl(it8, "NUMBER_OF_SETS", 10); + cmsIT8SetPropertyDbl(it8, "NUMBER_OF_FIELDS", Row); + + cmsIT8SetDataFormat(it8, 0, "SAMPLE_ID"); + cmsIT8SetDataFormat(it8, 1, "RGB_R"); + cmsIT8SetDataFormat(it8, 2, "RGB_G"); + cmsIT8SetDataFormat(it8, 3, "RGB_B"); + + for (i=0; i < 10; i++) { + + char Patch[20]; + + sprintf(Patch, "P%d", i); + + cmsIT8SetDataRowCol(it8, i, 0, Patch); + cmsIT8SetDataRowColDbl(it8, i, 1, i); + cmsIT8SetDataRowColDbl(it8, i, 2, i); + cmsIT8SetDataRowColDbl(it8, i, 3, i); + } + + cmsIT8SaveToFile(it8, "TEST.IT8"); + cmsIT8Free(it8); + + it8 = cmsIT8LoadFromFile(0, "TEST.IT8"); + if (it8 == NULL) return 0; + + /* Read */ + cmsIT8GetDataRowColDbl(it8,Row,Col); + cmsIT8GetPropertyDbl(it8, "DESCRIPTOR"); + cmsIT8GetDataDbl(it8, "P3", "RGB_G"); + + cmsIT8Free(it8); + return 1; +} diff --git a/projects/lcms/cms_cie_cam02_fuzzer.c b/projects/lcms/cms_cie_cam02_fuzzer.c new file mode 100644 index 000000000..3a2e2614d --- /dev/null +++ b/projects/lcms/cms_cie_cam02_fuzzer.c @@ -0,0 +1,51 @@ +/* Copyright 2023 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include "lcms2.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + if (size < sizeof(cmsViewingConditions)) { + return 0; + } + + // Define and initialize the viewing conditions structure + cmsViewingConditions viewingConditions; + viewingConditions.whitePoint.X = data[0]/ 255.0; + viewingConditions.whitePoint.Y = data[1]/ 255.0; + viewingConditions.whitePoint.Z = data[2]/ 255.0; + viewingConditions.Yb = data[3] / 255.0; + viewingConditions.La = data[4]/ 255.0; + viewingConditions.surround = data[5] % 4 + 1; //from 1 to 4 + viewingConditions.D_value = data[6] / 255.0; + + cmsContext context = cmsCreateContext(NULL, NULL); + + cmsHANDLE hModel = cmsCIECAM02Init(context, &viewingConditions); + + if (hModel) { + // Perform forward and reverse CAM02 transformations with appropriate input data + cmsCIEXYZ inputXYZ; + inputXYZ.X = data[0]/ 255.0; // Random value between 0 and 1 + inputXYZ.Y = data[1] / 255.0; + inputXYZ.Z = data[2] / 255.0; + cmsJCh outputJCh; + cmsCIEXYZ outputXYZ; + cmsCIECAM02Forward(hModel, &inputXYZ, &outputJCh); + cmsCIECAM02Reverse(hModel, &outputJCh, &outputXYZ); + cmsCIECAM02Done(hModel); + } + cmsDeleteContext(context); + + return 0; +} diff --git a/projects/lcms/cms_devicelink_fuzzer.c b/projects/lcms/cms_devicelink_fuzzer.c new file mode 100644 index 000000000..fc9646356 --- /dev/null +++ b/projects/lcms/cms_devicelink_fuzzer.c @@ -0,0 +1,32 @@ +/* Copyright 2023 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "lcms2.h" +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + if (size < 4) { + return 0; + } + + // cmsCreateInkLimitingDeviceLink + cmsFloat64Number limit = *((const uint32_t *)data) % 401; + + cmsHPROFILE limitingDeviceLinkProfile = + cmsCreateInkLimitingDeviceLink(cmsSigCmykData, limit); + if (limitingDeviceLinkProfile) { + cmsCloseProfile(limitingDeviceLinkProfile); + } + + return 0; +} diff --git a/projects/lcms/cms_dict_fuzzer.c b/projects/lcms/cms_dict_fuzzer.c new file mode 100644 index 000000000..635b5150a --- /dev/null +++ b/projects/lcms/cms_dict_fuzzer.c @@ -0,0 +1,95 @@ +/* Copyright 2023 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include +#include "lcms2.h" + +wchar_t* generateWideString(const char* characters, const uint8_t *data){ + if (!characters){ + return NULL; + } + + char stringToWide[10]; + for (int i = 0; i < 9; i++){ + stringToWide[i] = characters[data[i] % 95]; + } + stringToWide[9] = '\0'; + + int requiredSize = mbstowcs(NULL, stringToWide, 0); + wchar_t* wideString = (wchar_t *)malloc((requiredSize + 1) * sizeof(wchar_t)); + mbstowcs(wideString, stringToWide, requiredSize + 1); + return wideString; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 27){ + return 0; + } + + cmsContext context = cmsCreateContext(NULL, (void *)data); + if (!context) { + return 0; + } + + // Create a Dictionary handle + cmsHANDLE hDict = cmsDictAlloc(context); + if (!hDict) { + return 0; + } + + + cmsMLU *mlu = cmsMLUalloc(hDict, 0); + if (!mlu) { + return 0; + } + + char* characters = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + wchar_t* wideString = generateWideString(characters, data); + cmsMLUsetWide(mlu, "en", "US", wideString); + free(wideString); + + + char ObtainedLanguage[3], ObtainedCountry[3]; + ObtainedLanguage[0] = characters[*(data+1) % 95]; + ObtainedLanguage[1] = characters[*(data+2) % 95]; + ObtainedLanguage[2] = characters[*(data) % 95]; + + ObtainedCountry[0] = characters[*(data+2) % 95]; + ObtainedCountry[1] = characters[*data % 95]; + ObtainedCountry[2] = characters[*(data+1) % 95]; + cmsMLUgetTranslation(mlu, "en", "US",ObtainedLanguage,ObtainedCountry); + cmsMLUtranslationsCount(mlu); + cmsMLUtranslationsCodes(mlu, *((uint32_t *)data), ObtainedLanguage, ObtainedCountry); + + cmsMLU* displayName = mlu; + cmsMLU* displayValue = mlu; + + //cmsDictAddEntry + wchar_t* name = generateWideString(characters, data + 9); + wchar_t* value = generateWideString(characters, data + 18); + cmsDictAddEntry(hDict, name, value, displayName, displayValue); + free(name); + free(value); + + //cmsDictDup + cmsHANDLE ResultDictDup = cmsDictDup(hDict); + if (ResultDictDup) { + cmsDictFree(ResultDictDup); + } + // Iterate over the Dictionary entries + const cmsDICTentry* entry = cmsDictGetEntryList(hDict); + cmsDictNextEntry(entry); + cmsMLUfree(mlu); + cmsDictFree(hDict); + return 0; +} diff --git a/projects/lcms/cms_gdb_fuzzer.c b/projects/lcms/cms_gdb_fuzzer.c new file mode 100644 index 000000000..c7a987ba2 --- /dev/null +++ b/projects/lcms/cms_gdb_fuzzer.c @@ -0,0 +1,43 @@ +/* Copyright 2023 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include "lcms2.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + if (size < 16) { + return 0; + } + //cmsGBDAlloc + cmsHANDLE hGDB = cmsGBDAlloc(NULL); + if (!hGDB){ + return 0; + } + //cmsGDBAddPoint + cmsCIELab Lab; + Lab.L = *((const uint32_t *)data); + Lab.a = *((const uint32_t *)data+1); + Lab.b = *((const uint32_t *)data+2); + cmsGDBAddPoint(hGDB, &Lab); + + //cmsGDBCheckPoint + cmsGDBCheckPoint(hGDB, &Lab); + + //cmsGDBCompute + cmsGDBCompute(hGDB, *((const uint32_t *)data+3)); + + //cmsGBDFree + cmsGBDFree(hGDB); + + return 0; +} diff --git a/projects/lcms/cms_md5_fuzzer.c b/projects/lcms/cms_md5_fuzzer.c new file mode 100644 index 000000000..29957a277 --- /dev/null +++ b/projects/lcms/cms_md5_fuzzer.c @@ -0,0 +1,26 @@ +/* Copyright 2023 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include "lcms2.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + cmsHPROFILE hProfile = cmsOpenProfileFromMem(data, size); + if (!hProfile){ + return 0; + } + //cmsMD5computeID + cmsMD5computeID(hProfile); + cmsCloseProfile(hProfile); + return 0; +} diff --git a/projects/lcms/cms_postscript_fuzzer.c b/projects/lcms/cms_postscript_fuzzer.c new file mode 100644 index 000000000..4df0523d9 --- /dev/null +++ b/projects/lcms/cms_postscript_fuzzer.c @@ -0,0 +1,44 @@ +/* Copyright 2023 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include "lcms2.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + if (size < 16) { + return 0; + } + + cmsContext context = cmsCreateContext(NULL, (void *)data); + if (!context){ + return 0; + } + + cmsHPROFILE hProfile = cmsOpenProfileFromMem(data, size); + if (!hProfile){ + return 0; + } + + uint32_t flags = *((const uint32_t *)data+2); + uint32_t intent = *((const uint32_t *)data+3) % 16; + + /* cmsGetPostScriptCSA */ + cmsUInt32Number result1 = cmsGetPostScriptCSA(context, hProfile, intent, flags, NULL, size); + /* cmsGetPostScriptCRD */ + cmsUInt32Number result2 = cmsGetPostScriptCRD(context, hProfile, intent, flags, NULL, size); + + cmsCloseProfile(hProfile); + cmsDeleteContext(context); + + return 0; +} diff --git a/projects/lcms/cms_transform_extended_fuzzer.c b/projects/lcms/cms_transform_extended_fuzzer.c index 2a20bc125..671b4d4c8 100644 --- a/projects/lcms/cms_transform_extended_fuzzer.c +++ b/projects/lcms/cms_transform_extended_fuzzer.c @@ -62,6 +62,18 @@ run_test(const uint8_t *data, dstProfile = cmsCreateLab4Profile(NULL); dstFormat = TYPE_Lab_DBL; } + else if (dstVal == 8){ + dstProfile = cmsCreate_OkLabProfile(NULL); + dstFormat = (FLOAT_SH(1)|COLORSPACE_SH(PT_MCH3)|CHANNELS_SH(3)|BYTES_SH(0)); + } + else if (dstVal == 9){ + dstProfile = cmsCreateNULLProfile(); + dstFormat = 0; + } + else if (dstVal == 10){ + dstProfile = cmsCreateBCHSWabstractProfile(17, 0, 1.2, 0, 3, 5000, 5000); + dstFormat = TYPE_Lab_DBL; + } else { dstProfile = cmsCreate_sRGBProfile(); dstFormat = TYPE_RGB_8; @@ -128,7 +140,7 @@ run_test(const uint8_t *data, cmsCIEXYZTRIPLE out[4]; cmsDoTransform(hTransform, input, out, 1); } - else if (dstFormat == TYPE_Lab_DBL) { + else if (dstFormat == TYPE_Lab_DBL || dstFormat == (FLOAT_SH(1)|COLORSPACE_SH(PT_MCH3)|CHANNELS_SH(3)|BYTES_SH(0))) { cmsCIELab Lab1; cmsDoTransform(hTransform, input, &Lab1, 1); } @@ -148,7 +160,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { uint32_t flags = *((const uint32_t *)data+0); uint32_t intent = *((const uint32_t *)data+1) % 16; - int decider = *((int*)data+2) % 10; + int decider = *((int*)data+2) % 11; data += 12; size -= 12; diff --git a/projects/lcms/cms_virtual_profile_fuzzer.c b/projects/lcms/cms_virtual_profile_fuzzer.c new file mode 100644 index 000000000..e391e86ce --- /dev/null +++ b/projects/lcms/cms_virtual_profile_fuzzer.c @@ -0,0 +1,88 @@ +/* Copyright 2023 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "lcms2.h" +#include + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + + if (size < 16) { + return 0; + } + + cmsHPROFILE hInProfile = cmsOpenProfileFromMem(data, size); + if (!hInProfile) { + return 0; + } + + cmsHPROFILE hOutProfile = cmsCreate_sRGBProfile(); + if (!hOutProfile) { + cmsCloseProfile(hInProfile); + return 0; + } + cmsColorSpaceSignature srcCS = cmsGetColorSpace(hInProfile); + cmsUInt32Number nSrcComponents = cmsChannelsOf(srcCS); + cmsUInt32Number srcFormat; + if (srcCS == cmsSigLabData) { + srcFormat = + COLORSPACE_SH(PT_Lab) | CHANNELS_SH(nSrcComponents) | BYTES_SH(0); + } else { + srcFormat = + COLORSPACE_SH(PT_ANY) | CHANNELS_SH(nSrcComponents) | BYTES_SH(1); + } + cmsHTRANSFORM hTransform = cmsCreateTransform( + hInProfile, srcFormat, hOutProfile, TYPE_BGR_8, + *((const uint32_t *)data + 3) % 16, *((const uint32_t *)data + 2)); + + cmsCloseProfile(hInProfile); + cmsCloseProfile(hOutProfile); + if (!hTransform) { + return 0; + } + + cmsFloat64Number version; + if (*((const uint32_t *)data + 3) % 2 == 0) { + version = 3.4; + } else { + version = 4.4; + } + + // cmsTransform2DeviceLink + cmsHPROFILE devicelinkProfile = cmsTransform2DeviceLink( + hTransform, version, *((const uint32_t *)data + 2)); + + // clean up + cmsDeleteTransform(hTransform); + if (devicelinkProfile) { + cmsCloseProfile(devicelinkProfile); + } + + // cmsCreateLinearizationDeviceLink + cmsToneCurve *tone = cmsBuildGamma(NULL, *((const uint32_t *)data + 3)); + if (!tone) { + return 0; + } + // 15 curves, so it can handle all color spaces + cmsToneCurve *rgb_curves[15] = {tone, tone, tone, tone, tone, + tone, tone, tone, tone, tone, + tone, tone, tone, tone, tone}; + cmsHPROFILE linearizationDeviceLinkProfile = + cmsCreateLinearizationDeviceLink(srcCS, rgb_curves); + + cmsFreeToneCurve(tone); + + if (linearizationDeviceLinkProfile) { + cmsCloseProfile(linearizationDeviceLinkProfile); + } + + return 0; +}