internationalcolorconsortium / demoiccmax Goto Github PK
View Code? Open in Web Editor NEWDemonstration Implementation for iccMAX color profiles
License: Other
Demonstration Implementation for iccMAX color profiles
License: Other
I'd like to start documenting enums in IccProfLib/icProfileHeader.h something like this, which could be hyperlinked to specification in generated docs, and wanted to check if the format and such before I start doing a bunch:
`
/**
/* Private tags*/
icSigEmbeddedV5ProfileTag = 0x49434335, /* 'ICC5' */
} icTagSignature;
`
I'm a newbie with color technology and I'm interested in IccMax SDK.
I want to create a CMYK icc profile from some patches like IT8/7.4 CGATS.
These days I study Icc Profiles specifications V2
, V4
, Color Management(Wiley)
and try to create an icc profile.
wow, it is a complex topic which involves a wide range of disciplines including computer science, numerical analysis, and colour science.
but I encountered some problems.
1)I don't know what data(format, values...) for APIs when I try to use these functions.
2)No any API document that I don't know how to start.
3)I can't find any examples for CMYK output icc profiling.
Q1)Do you have any plan to design CxF3 SDK for reading and writing?
Q2)Could you give me advice about creating CMYK output icc profile?
Thanks
Severity: Reference Implementation | High
A heap buffer overflow has been identified in the CIccCLUT::Interp3d method. This vulnerability can lead to potential crashes or undefined behavior when certain conditions are met.
Utilize the CIccCLUT::Interp3d method on a CIccCLUT object under conditions where m_nNumPoints * m_nOutput equals 0.
Attempt to access indices beyond the allocated size of the m_pData array.
(lldb) fr se 5
frame #5: 0x0000000100e8c982 libIccProfLib2.2.dylib`CIccCLUT::Interp3d(this=0x0000614000000440, destPixel=0x00007ff7bfefde80, srcPixel=0x00007ff7bfefde80) const at IccTagLut.cpp:2594:27
2591 return;
2592 }
2593
-> 2594 destPixel[i] = p[n000] * dF0 + p[n001] * dF1 + p[n010] * dF2 + p[n011] * dF3 +
2595 p[n100] * dF4 + p[n101] * dF5 + p[n110] * dF6 + p[n111] * dF7;
2596 }
2597 }
(lldb) frame variable
(CIccCLUT *) this = 0x0000614000000440
(icFloatNumber *) destPixel = 0x00007ff7bfefde80
(const icFloatNumber *) srcPixel = 0x00007ff7bfefde80
(icUInt8Number) mx = '\xff'
(icUInt8Number) my = '\xff'
(icUInt8Number) mz = '\xff'
(icFloatNumber) x = 0
(icFloatNumber) y = 0
(icFloatNumber) z = 0
(icUInt32Number) ix = 0
(icUInt32Number) iy = 0
(icUInt32Number) iz = 0
(icFloatNumber) u = 0
(icFloatNumber) t = 0
(icFloatNumber) s = 0
(icFloatNumber) ns = 1
(icFloatNumber) nt = 1
(icFloatNumber) nu = 1
(icFloatNumber) dF0 = 1
(icFloatNumber) dF1 = 0
(icFloatNumber) dF2 = 0
(icFloatNumber) dF3 = 0
(icFloatNumber) dF4 = 0
(icFloatNumber) dF5 = 0
(icFloatNumber) dF6 = 0
(icFloatNumber) dF7 = 0
(icUInt32Number) maxIndex = 50331647
(icFloatNumber *) pStart = 0x00006020000002d0
(int) i = 0
(icFloatNumber *) p = 0x00006020000002d0
(lldb) dis -s $pc-32 -c 24
libIccProfLib2.2.dylib`CIccCLUT::Interp3d:
0x100e8c962 <+2978>: loopne 0x100e8c961 ; <+2977> at IccTagLut.cpp
Severity: Reference Implementation | High
A segmentation fault (SEGV) has been identified in the CIccXformMatrixTRC::GetCurve method within the IccCmm.cpp file, specifically at line 5240. The crash appears to be due to a read memory access at the zero address, suggesting a potential null pointer dereference.
Invoke the CIccXformMatrixTRC::GetCurve method with specific input that triggers the SEGV.
The exact input causing this behavior needs further investigation.
Observe the crash due to a segmentation fault.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==59095==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x000104511cd4 bp 0x7ff7bc556550 sp 0x7ff7bc556500 T0)
==59095==The signal is caused by a READ memory access.
==59095==Hint: address points to the zero page.
#0 0x104511cd4 in CIccXformMatrixTRC::GetCurve(unsigned int) const IccCmm.cpp:5240
#1 0x10451107a in CIccXformMatrixTRC::Begin() IccCmm.cpp:5100
#2 0x1045394a8 in CIccCmm::Begin(bool, bool) IccCmm.cpp:8297
#3 0x1045905a4 in CIccEvalCompare::EvaluateProfile(CIccProfile*, unsigned char, icRenderingIntent, icXformInterp, bool) IccEval.cpp:110
#4 0x104591419 in CIccEvalCompare::EvaluateProfile(char const*, unsigned char, icRenderingIntent, icXformInterp, bool) IccEval.cpp:197
#5 0x1039ae0d2 in main iccRoundTrip.cpp:168
#6 0x7ff80dee53a5 in start+0x795 (dyld:x86_64+0xfffffffffff5c3a5)
==59095==Register values:
rax = 0x0000000000000000 rbx = 0x00007ff7bc556900 rcx = 0x0000100000000000 rdx = 0x0000000000000000
rdi = 0x00007ff7bc5562c0 rsi = 0x00007ff7bc556320 rbp = 0x00007ff7bc556550 rsp = 0x00007ff7bc556500
r8 = 0x0000604000001610 r9 = 0xf3f3f300f1f1f100 r10 = 0x00007fffffffffff r11 = 0xffffffffffffff20
r12 = 0x00007ff7bc557590 r13 = 0x0000000000000000 r14 = 0x00000001039addb0 r15 = 0x00007ff7bc557710
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV IccCmm.cpp:5240 in CIccXformMatrixTRC::GetCurve(unsigned int) const
The program crashes with an AddressSanitizer report indicating a SEGV in CIccXformMatrixTRC::GetCurve.
The memory access causing the SEGV is a read operation from address 0x000000000000, which points to the zero page.
The crash occurs specifically at line IccCmm.cpp:5240.
Dereferencing a null pointer or uninitialized pointer in the CIccXformMatrixTRC::GetCurve method.
An invalid curve index or parameter passed to the method, leading to out-of-bounds memory access.
Memory corruption elsewhere in the application affecting the behavior of the CIccXformMatrixTRC::GetCurve method.
Bug Report: Undefined Behavior in IccTagLut.cpp and IccProfileXml.cpp
Severity: Reference Implementation | High
The UndefinedBehaviorSanitizer tool has identified multiple instances of undefined behavior within the IccTagLut.cpp and IccProfileXml.cpp files.
File: IccTagLut.cpp
Line: 1798
Issue: Index -1 is being used to access an array of type icUInt32Number[16], which results in out-of-bounds access.
File: IccTagLut.cpp
Line: 1799
Issue: Index -1 is being used to access an array of type icUInt8Number[16], which results in out-of-bounds access.
File: IccProfileXml.cpp
Line: 128
Issue: Loading a value 2543294359 which is not a valid value for the type icPlatformSignature.
File: IccProfileXml.cpp
Line: 129
Issue: Loading a value 2543294359 which is not a valid value for the type icPlatformSignature.
Severity: Reference Implementation | High
A heap buffer overflow has been identified in the function CIccXmlArrayType::ParseText at IccUtilXml.cpp:1003:10. This function is designed to parse text into an array, with a specific instantiation for unsigned short. The buffer overflow occurs during the parsing process, specifically in a loop condition where a buffer bound check fails, leading to an out-of-bounds read.
Variables at Crash Time:
pBuf: A pointer to the buffer where parsed data is stored.
nSize: The size of the buffer (4096).
szText: A pointer to the text being parsed.
n: Counter variable, which is also 4096 at the time of the crash.
Crash Location in Code:
The crash occurs in the loop: while (*szText && n<nSize) {.
The error indicates that when n equals nSize, the loop still attempts to read *szText, which is beyond the allocated buffer.
Testing % ./iccFromXML mcs/17ChanWithSpots-MVIS.xml mcs/17ChanWithSpots-MVIS.icc
mcs % ../iccFromXML 17ChanWithSpots-MVIS.xml 17ChanWithSpots-MVIS.icc
...
SUMMARY: AddressSanitizer: heap-buffer-overflow IccUtilXml.cpp:1003 in CIccXmlArrayType<unsigned short, (icTagTypeSignature)1969828150>::ParseText(unsigned short*, unsigned int, char const*)
Patch in PR #64
./iccSpecSepToTiff out.tif 0 0 Overprint/17ChanData.txt 1 2 ~/Documents/2225-original.icc
Arithmetic Error:
The crashing line (-> 0x100006251 <+1585>: idivl 0x230(%rbx)) suggests a division operation.
The corresponding source code line (120 n = (end-start)/step + 1;) is performing a division operation. If step is zero, this operation will cause a division by zero error, leading to an FPE.
mcs % lldb -- ../../Build/Tools/IccFromXml/iccFromXML 18ChanWithSpots-MVIS.xml 18ChanWithSpots-MVIS.icc
(lldb) target create "../../Build/Tools/IccFromXml/iccFromXML"
Current executable set to 'DemoIccMAX-master/Build/Tools/IccFromXml/iccFromXML' (x86_64).
(lldb) settings set -- target.run-args "18ChanWithSpots-MVIS.xml" "18ChanWithSpots-MVIS.icc"
(lldb) r
Process 79070 launched: 'DemoIccMAX-master/Build/Tools/IccFromXml/iccFromXML' (x86_64)
=================================================================
==79070==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x00010040340c at pc 0x000100990dc5 bp 0x7ff7bfefac70 sp 0x7ff7bfefac68
READ of size 1 at 0x00010040340c thread T0
#0 0x100990dc4 in CIccXmlArrayType<float, (icTagTypeSignature)1717793824>::ParseText(float*, unsigned int, char const*) IccUtilXml.cpp:1062
#1 0x100992462 in CIccXmlArrayType<float, (icTagTypeSignature)1717793824>::ParseTextArrayNum(char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccUtilXml.cpp:818
#2 0x10094e784 in icCLutFromXml(_xmlNode*, int, int, icConvertType, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccTagXml.cpp:3544
#3 0x1008b46ff in CIccMpeXmlExtCLUT::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccMpeXml.cpp:1935
#4 0x1008bf990 in CIccMpeXmlCalculator::ParseImport(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccMpeXml.cpp:2577
#5 0x1008d170f in CIccMpeXmlCalculator::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccMpeXml.cpp:3094
#6 0x10095561d in CIccTagXmlMultiProcessElement::ParseElement(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccTagXml.cpp:4081
#7 0x1009567b9 in CIccTagXmlMultiProcessElement::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccTagXml.cpp:4141
#8 0x10090b119 in CIccProfileXml::ParseTag(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccProfileXml.cpp:711
#9 0x10090c924 in CIccProfileXml::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccProfileXml.cpp:820
#10 0x10090cd5b in CIccProfileXml::LoadXml(char const*, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*) IccProfileXml.cpp:877
#11 0x10000406c in main IccFromXml.cpp:68
#12 0x7ff816e9f365 in start+0x795 (dyld:x86_64+0xfffffffffff5c365)
0x00010040340c is located 0 bytes after 2259980-byte region [0x0001001db800,0x00010040340c)
allocated by thread T0 here:
#0 0x101743a20 in wrap_malloc+0xa0 (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0xdca20)
#1 0x10094d4f1 in icCLutFromXml(_xmlNode*, int, int, icConvertType, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccTagXml.cpp:3439
#2 0x1008b46ff in CIccMpeXmlExtCLUT::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccMpeXml.cpp:1935
#3 0x1008bf990 in CIccMpeXmlCalculator::ParseImport(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccMpeXml.cpp:2577
#4 0x1008d170f in CIccMpeXmlCalculator::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccMpeXml.cpp:3094
#5 0x10095561d in CIccTagXmlMultiProcessElement::ParseElement(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccTagXml.cpp:4081
#6 0x1009567b9 in CIccTagXmlMultiProcessElement::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccTagXml.cpp:4141
#7 0x10090b119 in CIccProfileXml::ParseTag(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccProfileXml.cpp:711
#8 0x10090c924 in CIccProfileXml::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&) IccProfileXml.cpp:820
#9 0x10090cd5b in CIccProfileXml::LoadXml(char const*, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*) IccProfileXml.cpp:877
#10 0x10000406c in main IccFromXml.cpp:68
#11 0x7ff816e9f365 in start+0x795 (dyld:x86_64+0xfffffffffff5c365)
SUMMARY: AddressSanitizer: heap-buffer-overflow IccUtilXml.cpp:1062 in CIccXmlArrayType<float, (icTagTypeSignature)1717793824>::ParseText(float*, unsigned int, char const*)
Shadow bytes around the buggy address:
0x000100403180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000100403200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000100403280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000100403300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x000100403380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x000100403400: 00[04]fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x000100403480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x000100403500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x000100403580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x000100403600: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x000100403680: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==79070==ABORTING
Assigned value is garbage or undefined 2
Called C++ object pointer is null 3
Dereference of null pointer 13
Garbage return value 1
Result of operation is garbage or undefined 4
Uninitialized argument value 6
Unix API 4
Bad deallocator 7
Double free 2
Memory leak 10
Use-after-free 8
Allocator sizeof operand mismatch 1
Dead assignment 28
Dead increment 3
Dead initialization 20
Dead nested assignment 1
https://bugs.chromium.org/p/project-zero/issues/detail?id=2225
https://bugs.chromium.org/p/project-zero/issues/detail?id=2226
https://srd.cx/cve-2022-26730/
https://srd.cx/cve-2023-32443/
Clang Static Analyzer Report https://xss.cx/2023/10/29/src/demomax-clang-static-analysis/
Pull Request #53
icFixXml function [CVE-2023-46602]
CIccPRMG::GetChroma function [CVE-2023-46603]
cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS="-g -fsanitize=address -fno-omit-frame-pointer -Wall -std=c++17 -fprofile-instr-generate -fcoverage-mapping" \
../Build/Cmake
make
DemoIccMAX-master/Build/Tools/IccFromXml/iccFromXml DemoIccMAX-master/Testing/CMYK-3DLUTs/CMYK-3DLUTs2.xml DemoIccMAX-master/Testing/CMYK-3DLUTs/CMYK-3DLUTs2.icc
DemoIccMAX-master/Build/Tools/IccFromXml/iccFromXml DemoIccMAX-master/Testing/ICS/Rec2100HlgFull-Part1.xml DemoIccMAX-master/Testing/ICS/Rec2100HlgFull-Part1.icc
DemoIccMAX-master/Build/Tools/IccFromXml/iccFromXml DemoIccMAX-master/Testing/ICS/Rec2100HlgFull-Part2.xml DemoIccMAX-master/Testing/ICS/Rec2100HlgFull-Part2.icc
DemoIccMAX-master/Build/Tools/IccFromXml/iccFromXml DemoIccMAX-master/Testing/Overprint/17ChanPart1.xml DemoIccMAX-master/Testing/Overprint/17ChanPart1.icc
// * Modified by @h02332 | David Hoyt
// determine granularity
if (!nGran) {
CIccTag* tag = pProfile->FindTag(icSigAToB0Tag + (nIntent == icAbsoluteColorimetric ? icRelativeColorimetric : nIntent));
if (!tag) {
std::cerr << "Tag is nullptr!" << std::endl;
return icCmmStatColorNotFound;
}
try {
if (dynamic_cast<CIccTagLut16*>(tag) != nullptr) {
std::cerr << "Unexpected object type detected!" << std::endl;
// If there's a method to get more details about the tag, use it. If not, skip this line.
// std::cerr << "Tag details: " << tag->GetDetails() << std::endl;
std::cerr << "Actual object type: " << typeid(*tag).name() << std::endl;
} else {
std::cerr << "Tag is not of expected type!" << std::endl;
std::cerr << "Actual object type: " << typeid(*tag).name() << std::endl;
}
} catch (const std::bad_typeid& e) {
std::cerr << "Caught bad_typeid exception: " << e.what() << std::endl;
return icCmmStatBadLutType;
}
CIccTagLutAtoB* pTag = dynamic_cast<CIccTagLutAtoB*>(tag);
// Check if pTag is valid. If it's not, we set nGran to default value and skip further checks.
if (!pTag || ndim == 3) {
nGran = 33;
} else {
CIccCLUT* pClut = pTag->GetCLUT();
nGran = pClut ? (pClut->GridPoints() + 2) : 33;
}
}
/**
******************************************************************************
* Name: CIccCLUT::Interp3d
*
* Purpose: Three dimensional interpolation function
*
* Args:
* Pixel = Pixel value to be found in the CLUT. Also used to store the result.
* Modified by @h02332 | David Hoyt
*******************************************************************************
*/
void CIccCLUT::Interp3d(icFloatNumber *destPixel, const icFloatNumber *srcPixel) const
{
icUInt8Number mx = m_MaxGridPoint[0];
icUInt8Number my = m_MaxGridPoint[1];
icUInt8Number mz = m_MaxGridPoint[2];
icFloatNumber x = UnitClip(srcPixel[0]) * mx;
icFloatNumber y = UnitClip(srcPixel[1]) * my;
icFloatNumber z = UnitClip(srcPixel[2]) * mz;
icUInt32Number ix = (icUInt32Number)x;
icUInt32Number iy = (icUInt32Number)y;
icUInt32Number iz = (icUInt32Number)z;
icFloatNumber u = x - ix;
icFloatNumber t = y - iy;
icFloatNumber s = z - iz;
if (ix == mx) {
ix--;
u = 1.0;
}
if (iy == my) {
iy--;
t = 1.0;
}
if (iz == mz) {
iz--;
s = 1.0;
}
icFloatNumber ns = (icFloatNumber)(1.0 - s);
icFloatNumber nt = (icFloatNumber)(1.0 - t);
icFloatNumber nu = (icFloatNumber)(1.0 - u);
icUInt32Number maxIndex = (mx + 1) * (my + 1) * (mz + 1) * m_nOutput - 1;
icFloatNumber dF0 = ns * nt * nu;
icFloatNumber dF1 = ns * nt * u;
icFloatNumber dF2 = ns * t * nu;
icFloatNumber dF3 = ns * t * u;
icFloatNumber dF4 = s * nt * nu;
icFloatNumber dF5 = s * nt * u;
icFloatNumber dF6 = s * t * nu;
icFloatNumber dF7 = s * t * u;
icFloatNumber *pStart = &m_pData[ix * n001 + iy * n010 + iz * n100];
for (int i = 0; i < m_nOutput; i++) {
icFloatNumber *p = pStart + i;
// Logging statements before the problematic line:
std::cout << "Pointer p: " << p << std::endl;
std::cout << "n000: " << n000 << ", Value: " << p[n000] << std::endl;
std::cout << "n001: " << n001 << ", Value: " << p[n001] << std::endl;
std::cout << "n010: " << n010 << ", Value: " << p[n010] << std::endl;
std::cout << "n011: " << n011 << ", Value: " << p[n011] << std::endl;
std::cout << "n100: " << n100 << ", Value: " << p[n100] << std::endl;
std::cout << "n101: " << n101 << ", Value: " << p[n101] << std::endl;
std::cout << "n110: " << n110 << ", Value: " << p[n110] << std::endl;
std::cout << "n111: " << n111 << ", Value: " << p[n111] << std::endl;
// ... Similarly for other indices ...
// Log the maximum possible index and the current index
std::cout << "Max Index: " << maxIndex << ", Current Index: " << (p + n111 - m_pData) << std::endl;
if (p + n111 > m_pData + maxIndex) {
std::cerr << "Potential overflow detected. Aborting." << std::endl;
return;
}
// Try to safely access the value at p[n000]
if (p + n000 > m_pData + maxIndex) {
std::cerr << "Potential overflow detected. Aborting." << std::endl;
return;
} else {
std::cout << "Value at (p + n000): " << p[n000] << std::endl;
}
if ((p + n001) >= (m_pData + maxIndex)) {
std::cerr << "Out-of-bounds access detected for n001." << std::endl;
}
if ((p + n010) >= (m_pData + maxIndex)) {
std::cerr << "Out-of-bounds access detected for n010." << std::endl;
}
if ((p + n011) >= (m_pData + maxIndex)) {
std::cerr << "Out-of-bounds access detected for n011." << std::endl;
}
if ((p + n100) >= (m_pData + maxIndex)) {
std::cerr << "Out-of-bounds access detected for n100." << std::endl;
}
if ((p + n101) >= (m_pData + maxIndex)) {
std::cerr << "Out-of-bounds access detected for n101." << std::endl;
}
if ((p + n110) >= (m_pData + maxIndex)) {
std::cerr << "Out-of-bounds access detected for n110." << std::endl;
}
if ((p + n111) >= (m_pData + maxIndex)) {
std::cerr << "Out-of-bounds access detected for n111." << std::endl;
}
// Add logging statements before the problematic line:
std::cout << "n000: " << n000 << std::endl;
std::cout << "n001: " << n001 << std::endl;
std::cout << "n010: " << n010 << std::endl;
std::cout << "n011: " << n011 << std::endl;
std::cout << "n100: " << n100 << std::endl;
std::cout << "n101: " << n101 << std::endl;
std::cout << "n110: " << n110 << std::endl;
std::cout << "n111: " << n111 << std::endl;
std::cout << "ix: " << ix << std::endl;
std::cout << "iy: " << iy << std::endl;
std::cout << "iz: " << iz << std::endl;
std::cout << "n001: " << n001 << std::endl;
std::cout << "n010: " << n010 << std::endl;
std::cout << "n100: " << n100 << std::endl;
std::cout << "pStart address: " << pStart << std::endl;
// Before the problematic line
std::cout << "Pointer p: " << p << std::endl;
std::cout << "n000: " << n000 << std::endl;
std::cout << "Address (p + n000): " << p + n000 << std::endl;
destPixel[i] = p[n000] * dF0 + p[n001] * dF1 + p[n010] * dF2 + p[n011] * dF3 +
p[n100] * dF4 + p[n101] * dF5 + p[n110] * dF6 + p[n111] * dF7;
}
}
// * Modified by @h02332 | David Hoyt
class CIccDumpXmlCLUT : public IIccCLUTExec
{
public:
CIccDumpXmlCLUT(std::string *xml, icConvertType nType, std::string blanks, icUInt16Number nSamples, icUInt8Number nPixelsPerRow)
{
m_xml = xml;
m_nType = nType;
m_blanks = blanks;
m_nSamples = nSamples;
m_nPixelsPerRow = nPixelsPerRow;
m_nCurPixel = 0;
}
virtual void PixelOp(icFloatNumber* pGridAdr, icFloatNumber* pData)
{
if (!pData) {
std::cerr << "Error: pData is nullptr." << std::endl;
return; // Early return if pData is null
}
int i;
char buf[128];
if (!(m_nCurPixel % m_nPixelsPerRow))
*m_xml += m_blanks;
switch(m_nType) {
case icConvert8Bit:
for (i=0; i<m_nSamples; i++) {
if (&pData[i] == nullptr) { // Check if accessing pData[i] is valid
std::cerr << "Error: pData[i] is out of bounds." << std::endl;
return;
}
sprintf(buf, " %3d", (icUInt8Number)(pData[i]*255.0 + 0.5));
*m_xml += buf;
}
break;
case icConvert16Bit:
for (i=0; i<m_nSamples; i++) {
sprintf(buf, " %5d", (icUInt16Number)(pData[i]*65535.0 + 0.5));
*m_xml += buf;
}
break;
case icConvertFloat:
default:
for (i=0; i<m_nSamples; i++) {
sprintf(buf, " %13.8f", pData[i]);
*m_xml += buf;
}
break;
}
m_nCurPixel++;
if (!(m_nCurPixel % m_nPixelsPerRow)) {
*m_xml += "\n";
}
}
void Finish()
{
if (m_nCurPixel % m_nPixelsPerRow) {
*m_xml += "\n";
}
}
std::string *m_xml;
icConvertType m_nType;
std::string m_blanks;
icUInt16Number m_nSamples;
icUInt8Number m_nPixelsPerRow;
icUInt32Number m_nCurPixel;
};
intext:"libiccxml" OR intext:"iccproflib" "International Color Consortium" filetype:pdf OR filetype:txt OR filetype:md OR filetype:xml OR filetype:txt OR filetype:cpp
"Libiccxml" OR "iccproflib"
"iccxmllib" OR "iccproflib"
I'm a newcomer in Icc profile.
I saw a lot of examples in IccProfLib and RefIccMax, but I still no any ideas to create an Icc profile.
I have a big problem that what kind of data for creating an Icc Profile.
All examples are compiled success, but I don't know how to use it. because I don't known how to prepare data for input.
for example: create_CLUT_profile command tool
Could you give me some advice?
thanks
Though I doubt this comes up in actual ICC profiles, I noticed that when parsing the B curves for the AtoB / BtoA LUT types, the offset realignment is wrong - it realigns based on the offset for the matrix instead of the B curve offset. You can see that the M & A curves are both correct and align to their respective offsets, so it's just the B curve that has this wrong. It's a minor thing that tripped me up when I was checking how the parsing code handles curve alignment.
See the diff below for the proposed fix. It feels a bit silly to do a pull request for a literal one-character change, but I can do that if it would be helpful.
diff --git a/IccProfLib/IccTagLut.cpp b/IccProfLib/IccTagLut.cpp
index 28bb070..3289370 100644
--- a/IccProfLib/IccTagLut.cpp
+++ b/IccProfLib/IccTagLut.cpp
@@ -3846,7 +3846,7 @@ bool CIccTagLutAtoB::Read(icUInt32Number size, CIccIO *pIO)
if (!pCurves[i]->Read(nEnd - pIO->Tell(), pIO))
return false;
- if (!pIO->Sync32(Offset[1]))
+ if (!pIO->Sync32(Offset[0]))
return false;
}
}
The Lut16 & Lut8 types are supposed to have special handling when the input color space is PCSXYZ, which is the only time the matrix coefficients get used. From what I can tell, this special handling exists but never actually loads the matrix coefficients in or loads new curves into the B curves section. Is this correct, in that "in-the-wild" ICC profiles don't ever actually use the matrix coefficients correctly and they should thus be ignored, or should they be loaded in? If they should be ignored, perhaps the code that generates a new matrix and curves can be removed?
I took the Lut8 section of code I'm talking about and annotated it below:
void CIccTagLut8::SetColorSpaces(icColorSpaceSignature csInput, icColorSpaceSignature csOutput)
{
if (csInput==icSigXYZData) {
int i;
if (!m_CurvesM && IsInputMatrix()) { //Transfer ownership of curves
m_CurvesM = m_CurvesB;
m_CurvesB = NULL;
// 👇 Why does this code allocate a new set of curves and do nothing with it?
LPIccCurve *pCurves = NewCurvesB();
CIccTagCurve *pCurve;
for (i=0; i<m_nInput; i++) {
pCurves[i] = pCurve = (CIccTagCurve*)CIccTag::Create(icSigCurveType);
pCurve->SetSize(0);
}
m_bUseMCurvesAsBCurves = true;
}
if (!m_Matrix) {
// 👇 Why does this code allocate a new matrix and do nothing with it?
CIccMatrix *pMatrix = NewMatrix();
for (i=0; i<9; i++) {
pMatrix->m_e[i] = icFtoD(m_XYZMatrix[i]);
}
pMatrix->m_bUseConstants=false;
}
}
else {
m_XYZMatrix[0] = m_XYZMatrix[4] = m_XYZMatrix[8] = icDtoF(1.0);
m_XYZMatrix[1] = m_XYZMatrix[2] = m_XYZMatrix[3] =
m_XYZMatrix[5] = m_XYZMatrix[6] = m_XYZMatrix[7] = 0;
}
CIccMBB::SetColorSpaces(csInput, csOutput);
}
I am considering using IccProfLib to process some ICC profiles in a program. I do not need the (de)serialization of ICC profiles to(from) XML that the IccLibXML extension provides.
Unfortunately to build the project for IccProfLib without modification, we are forced to build IccXML, which means that we must have libxml2 installed as a dependency. This makes it more difficult to build IccProfLib using a cross compiler, because we must first cross-compile libxml2.
It would be nice to have a CMake var, for example, ENABLE_ICCXML
that we can set to OFF
(default ON
to preserve existing behavior).
for me the correct export path was export LD_LIBRARY_PATH=$HOME/.local/lib64:$LD_LIBRARY_PATH
this is for the debug build flag in the instructions example
I noticed that the IccProflib reports an error in v2.x.y profile versions of the ICC profiles if the bytes corresponding to the "Profile ID" of v4.3.0 (bytes 84..99) are not zero and do not correspond to the md5 sum above the ICC header.
However, the spec for v2.x.y states that bytes 84..99 are reserved (see https://www.color.org/ICC_Minor_Revision_for_Web.pdf, page 12).
My question:
Is interpreting bytes 84..99 in v2.x.y profile versions a bug in IccProfLib?
If no, what did I interpret wrong?
Here comes a list of potential headers to be installed into a system location for API users. Comments are appreciated on what to exclude or what is missed.
SET( HEADERS_PUBLIC
${SRC_PATH}/IccProfLib/IccApplyBPC.h
${SRC_PATH}/IccProfLib/IccArrayBasic.h
${SRC_PATH}/IccProfLib/IccArrayFactory.h
${SRC_PATH}/IccProfLib/IccCAM.h
${SRC_PATH}/IccProfLib/IccCmm.h
${SRC_PATH}/IccProfLib/IccConvertUTF.h
${SRC_PATH}/IccProfLib/IccDefs.h
${SRC_PATH}/IccProfLib/IccEncoding.h
${SRC_PATH}/IccProfLib/IccEnvVar.h
${SRC_PATH}/IccProfLib/IccEval.h
${SRC_PATH}/IccProfLib/IccIO.h
${SRC_PATH}/IccProfLib/IccMatrixMath.h
${SRC_PATH}/IccProfLib/IccMpeACS.h
${SRC_PATH}/IccProfLib/IccMpeBasic.h
${SRC_PATH}/IccProfLib/IccMpeCalc.h
${SRC_PATH}/IccProfLib/IccMpeFactory.h
${SRC_PATH}/IccProfLib/IccMpeSpectral.h
${SRC_PATH}/IccProfLib/IccPcc.h
${SRC_PATH}/IccProfLib/IccPrmg.h
${SRC_PATH}/IccProfLib/IccProfile.h
${SRC_PATH}/IccProfLib/IccProfLibConf.h
${SRC_PATH}/IccProfLib/IccProfLibVer.h
${SRC_PATH}/IccProfLib/IccSolve.h
${SRC_PATH}/IccProfLib/IccSparseMatrix.h
${SRC_PATH}/IccProfLib/IccStructBasic.h
${SRC_PATH}/IccProfLib/IccStructFactory.h
${SRC_PATH}/IccProfLib/IccTagBasic.h
${SRC_PATH}/IccProfLib/IccTagComposite.h
${SRC_PATH}/IccProfLib/IccTagDict.h
${SRC_PATH}/IccProfLib/IccTagFactory.h
${SRC_PATH}/IccProfLib/IccTag.h
${SRC_PATH}/IccProfLib/IccTagLut.h
${SRC_PATH}/IccProfLib/IccTagMPE.h
${SRC_PATH}/IccProfLib/IccTagProfSeqId.h
${SRC_PATH}/IccProfLib/IccUtil.h
${SRC_PATH}/IccProfLib/IccWrapper.h
${SRC_PATH}/IccProfLib/IccXformFactory.h
${SRC_PATH}/IccProfLib/icProfileHeader.h
${SRC_PATH}/IccProfLib/MainPage.h
)
It might be worthwhile to consider related library names, as the two previous projects moved into a single on.
SampleICC (past): libSampleICC.a
IccXML (past): libIccXML.a
IccProfLib (now): libIccProfLib.a
proposed:
IccProfLib: libRefIccMaxProf.a
IccXML: libRefIccMaxXml.a
other ideas welcome
Honestly I think RefIccMAX can do with a good bit of code refactoring. I think I understand ICC profiles less after reading the reference implementation here.
Originally posted by @hypernewbie in #12 (comment)
Tested on OS - Ubuntu 22.04 LTS 5.15.0-72-generic #79-Ubuntu SMP x86_64 x86_64 x86_64 GNU/Linux
A heap based buffer overflow was identified on IccToXml
while fuzzing the vulnerability is triggered with a crafted .icc
profile file. Attaching the same for reference.
GDB:
(gdb) r sample.icc /dev/null
Starting program: /DemoIccMAX/Build/Cmake/Tools/IccToXml/iccToXml sample.icc /dev/null
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7f6bc9c in icCurvesToXml (xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<IccProfile>\n <Header>\n <PreferredCMMType>ADBE</PreferredCMMType>\n <ProfileVersion>4.20</ProfileVersion>\n <ProfileDeviceClass>scnr</ProfileDeviceClass>"..., szName=0x7ffff7f99874 "MCurves", pCurves=pCurves@entry=0x61f7a0, numCurve=<optimized out>, numCurve@entry=3, nType=nType@entry=icConvertVariable, blanks=...) at /DemoIccMAX/IccXML/IccLibXML/IccTagXml.cpp:2991
2991 IIccExtensionTag *pTag = pCurves[i]->GetExtension();
(gdb)
(gdb) bt
#0 0x00007ffff7f6bc9c in icCurvesToXml (
xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<IccProfile>\n <Header>\n <PreferredCMMType>ADBE</PreferredCMMType>\n <ProfileVersion>4.20</ProfileVersion>\n <ProfileDeviceClass>scnr</ProfileDeviceClass>"..., szName=0x7ffff7f99874 "MCurves", pCurves=pCurves@entry=0x61f7a0, numCurve=<optimized out>, numCurve@entry=3,
nType=nType@entry=icConvertVariable, blanks=...) at /DemoIccMAX/IccXML/IccLibXML/IccTagXml.cpp:2991
#1 0x00007ffff7f6cfc7 in icMBBToXml (
xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<IccProfile>\n <Header>\n <PreferredCMMType>ADBE</PreferredCMMType>\n <ProfileVersion>4.20</ProfileVersion>\n <ProfileDeviceClass>scnr</ProfileDeviceClass>"..., pMBB=pMBB@entry=0x61f4b0, nType=nType@entry=icConvertVariable, blanks=..., bSaveGridPoints=true)
at /DemoIccMAX/IccXML/IccLibXML/IccTagXml.cpp:3151
#2 0x00007ffff7f6f86c in CIccTagXmlLutAtoB::ToXml (this=0x61f4b0,
xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<IccProfile>\n <Header>\n <PreferredCMMType>ADBE</PreferredCMMType>\n <ProfileVersion>4.20</ProfileVersion>\n <ProfileDeviceClass>scnr</ProfileDeviceClass>"..., blanks=...) at /DemoIccMAX/IccXML/IccLibXML/IccTagXml.cpp:3798
#3 0x00007ffff7f58331 in CIccProfileXml::ToXmlWithBlanks (this=<optimized out>, this@entry=0x7fffffffe1e8, xml=..., blanks=...)
at /DemoIccMAX/IccXML/IccLibXML/IccProfileXml.cpp:264
#4 0x00007ffff7f557ec in CIccProfileXml::ToXml (this=0x7fffffffe1e8,
xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<IccProfile>\n <Header>\n <PreferredCMMType>ADBE</PreferredCMMType>\n <ProfileVersion>4.20</ProfileVersion>\n <ProfileDeviceClass>scnr</ProfileDeviceClass>"...) at /DemoIccMAX/IccXML/IccLibXML/IccProfileXml.cpp:79
#5 0x0000000000403696 in main (argc=<optimized out>, argv=0x7fffffffe3b8) at /DemoIccMAX/IccXML/CmdLine/IccToXml/IccToXml.cpp:38
(gdb) i r
rax 0x61f7a0 6420384
rbx 0x1 1
rcx 0x1 1
rdx 0x624 1572
rsi 0x60c010 6340624
rdi 0x0 0
rbp 0x1 0x1
rsp 0x7fffffffd3f0 0x7fffffffd3f0
r8 0x624580 6440320
r9 0x0 0
r10 0x7fffffffc9b0 140737488341424
r11 0xb6ff62935fcf2779 -5260377454638323847
r12 0x7fffffffd488 140737488344200
r13 0x61f7e0 6420448
r14 0x6 6
r15 0x7fffffffd478 140737488344184
rip 0x7ffff7f6bc9c 0x7ffff7f6bc9c <icCurvesToXml(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, char const*, CIccCurve**, int, icConvertType, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)+588>
eflags 0x10202 [ IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb)
rax 0x61f7a0 6420384
rbx 0x1 1
rcx 0x1 1
rdx 0x624 1572
rsi 0x60c010 6340624
rdi 0x0 0
rbp 0x1 0x1
rsp 0x7fffffffd3f0 0x7fffffffd3f0
r8 0x624580 6440320
r9 0x0 0
r10 0x7fffffffc9b0 140737488341424
r11 0xb6ff62935fcf2779 -5260377454638323847
r12 0x7fffffffd488 140737488344200
r13 0x61f7e0 6420448
r14 0x6 6
r15 0x7fffffffd478 140737488344184
rip 0x7ffff7f6bc9c 0x7ffff7f6bc9c <icCurvesToXml(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, char const*, CIccCurve**, int, icConvertType, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)+588>
eflags 0x10202 [ IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb)
ASAN:
==115400==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000118 at pc 0x7ffff7f03d71 bp 0x7fffffffb7d0 sp 0x7fffffffb7c8
READ of size 8 at 0x602000000118 thread T0
#0 0x7ffff7f03d70 in icCurvesToXml(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, char const*, CIccCurve**, int, icConvertType, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) DemoIccMAX/IccXML/IccLibXML/IccTagXml.cpp:2991:32
#1 0x7ffff7f07440 in icMBBToXml(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, CIccMBB*, icConvertType, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool) DemoIccMAX/IccXML/IccLibXML/IccTagXml.cpp:3151:12
#2 0x7ffff7f10e17 in CIccTagXmlLutAtoB::ToXml(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) DemoIccMAX/IccXML/IccLibXML/IccTagXml.cpp:3798:13
#3 0x7ffff7ebff07 in CIccProfileXml::ToXmlWithBlanks(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) DemoIccMAX/IccXML/IccLibXML/IccProfileXml.cpp:264:27
#4 0x7ffff7eb930e in CIccProfileXml::ToXml(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) DemoIccMAX/IccXML/IccLibXML/IccProfileXml.cpp:79:10
#5 0x4d3da5 in main DemoIccMAX/IccXML/CmdLine/IccToXml/IccToXml.cpp:38:16
#6 0x7ffff7319d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#7 0x7ffff7319e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#8 0x4224d4 in _start (DemoIccMAX/Build/Cmake/Tools/IccToXml/iccToXml+0x4224d4)
0x602000000118 is located 0 bytes to the right of 8-byte region [0x602000000110,0x602000000118)
allocated by thread T0 here:
#0 0x4d128d in operator new[](unsigned long) (DemoIccMAX/Build/Cmake/Tools/IccToXml/iccToXml+0x4d128d)
#1 0x7ffff7d260b8 in CIccMBB::NewCurvesM() DemoIccMAX/IccProfLib/IccTagLut.cpp:3595:15
#2 0x7ffff7d260b8 in CIccTagLutAtoB::Read(unsigned int, CIccIO*) DemoIccMAX/IccProfLib/IccTagLut.cpp:3877:27
#3 0x7ffff7c240e3 in CIccProfile::LoadTag(IccTagEntry*, CIccIO*, bool) DemoIccMAX/IccProfLib/IccProfile.cpp:1188:14
#4 0x7ffff7c275d0 in CIccProfile::Read(CIccIO*, bool) DemoIccMAX/IccProfLib/IccProfile.cpp:748:10
SUMMARY: AddressSanitizer: heap-buffer-overflow DemoIccMAX/IccXML/IccLibXML/IccTagXml.cpp:2991:32 in icCurvesToXml(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, char const*, CIccCurve**, int, icConvertType, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
Shadow bytes around the buggy address:
0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c047fff8000: fa fa 00 fa fa fa 00 fa fa fa 00 fa fa fa 00 fa
0x0c047fff8010: fa fa fd fa fa fa fd fa fa fa 00 04 fa fa 00 fa
=>0x0c047fff8020: fa fa 00[fa]fa fa fd fa fa fa fa fa fa fa fa fa
0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==115400==ABORTING
iccProfLib uses C++11 features (e.g. {}
syntax for object initialisation), but the projects use the Xcode default for C++ dialect, which is C++98.
iccProfLib also contains invalid code, that casts enums to integers and tries to use values outside their valid range (e.g. in GetMeasurementFlareName
) which is undefined behaviour. Clang errors on this by default, which can technically be silenced with -Wno-enum-constexpr-conversion
although that's not a real solution (Clang doesn't guarantee the runtime behaviour will be as the code expects) and a performance pessimisation.
Beyond that, the iccProfLib builds as x86-64 only but all the subprojects (e.g. IccApplyNamedCmm) use the default value for the target architectures (which is currently x86_64 and arm64). So they all fail to link due to the missing arm64 slice.
RefIccMAXCmm also fails to build because it's using ColorSync types (e.g. CMError
) which seemingly don't exist anymore (they were deprecated more than a decade ago, so I assume they've finally be outright removed from the framework).
The macOS deployment target is also being set to (or defaulting to?) 10.11, but modern Xcode doesn't support earlier than 10.13. This nominally just results in a warning, although it's possible it's screwing other things up too (hard to tell with so many other errors occurring in parallel).
IccLibXML.xcodeproj is missing targets for the actual command line tools - IccToXML et al.
Hi, I am trying to figure out how to programatically create a "Spot Color" for use when generating a PDF programatically. I am hoping someone here may have some knowledge on whether it is possible to do that by creating a custom color space.
Unfortunately I know very little about creating a custom color space.
The application is developed in Swift on macOS and generates print ready PDF files. Some printing required the use of SPOT colors - so solid colors that cannot be printed using normal CMYK printing.
The printers need the PDF to have these elements printed using a custom named color space so their print systems will automatically pick up the components to be printed with the spot color.
I am hoping that perhaps I can use the DemoIccMAX libraries to create a custom profile of some sort that can be installed on macOS and subsequently used programatically.
So the related questions I have are:
Is it possible to define a custom profile with a single color in XML and then use the DemoIccMAX libraries to generate a icc profile that can be installed on macOS. This single color can be green - the print shop doesn't use the actual color, they just use the specific name to identify the objects to be printed. They have a separate process to load the actual ink color. Typically this is required when printing white on black substrate - they need to use a solid white ink since you can't do white in CMYK.
Is it possible to use these libraries to generate such a profile on the fly such that it can be used programatically by the macOS APIs such as
CGColorSpace.init(iccData:)
Creates an ICC-based color space using the ICC profile contained in the specified data.
Any assistance will be much appreciated.
typo in local variable name: is bcdPair, should be bcdpair (all lower).
Hi,
I want to know how to create an ICC Profile by scanning a color chart with DemoIccMAX.
Thank you
Best regards
Compile Errors from PR #69
Commit b90ac3933da99179df26351c39d8d9d706ac1cc6
Passing Compile in PR #66
Commit f891074a0f1c9d61a3dfa53749265f8c14ed4ee6
cd /tmp
wget https://github.com/InternationalColorConsortium/DemoIccMAX/archive/f891074a0f1c9d61a3dfa53749265f8c14ed4ee6.zip
unzip f891074a0f1c9d61a3dfa53749265f8c14ed4ee6.zip
cd DemoIccMAX-f891074a0f1c9d61a3dfa53749265f8c14ed4ee6/Build
cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-g -fsanitize=address,undefined -fno-omit-frame-pointer -Wall" -Wno-dev Cmake
make -j$(nproc) 2>&1 | grep 'error:'
Result: Passing
cd /tmp
wget https://github.com/InternationalColorConsortium/DemoIccMAX/archive/b90ac3933da99179df26351c39d8d9d706ac1cc6.zip
unzip b90ac3933da99179df26351c39d8d9d706ac1cc6.zip
cd DemoIccMAX-b90ac3933da99179df26351c39d8d9d706ac1cc6/Build
cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-g -fsanitize=address,undefined -fno-omit-frame-pointer -Wall" -Wno-dev Cmake
make -j$(nproc) 2>&1 | grep 'error:'
...
DemoIccMAX-b90ac3933da99179df26351c39d8d9d706ac1cc6/Build/Cmake/../../IccProfLib/IccCmm.h:1246:23: error: unknown type name 'CIccXformNDLut'
DemoIccMAX-b90ac3933da99179df26351c39d8d9d706ac1cc6/Build/Cmake/../../IccProfLib/IccCmm.h:1246:23: error: unknown type name 'CIccXformNDLut'
DemoIccMAX-b90ac3933da99179df26351c39d8d9d706ac1cc6/Build/Cmake/../../IccProfLib/IccCmm.h:1246:23: error: unknown type name 'CIccXformNDLut'
DemoIccMAX-b90ac3933da99179df26351c39d8d9d706ac1cc6/Build/Cmake/../../IccProfLib/IccCmm.h:1246:23: error: unknown type name 'CIccXformNDLut'
DemoIccMAX-b90ac3933da99179df26351c39d8d9d706ac1cc6/IccProfLib/IccCmm.cpp:1923:22: error: out-of-line definition of 'CIccApplyNDLutXform' does not match any declaration in 'CIccApplyNDLutXform'
DemoIccMAX-b90ac3933da99179df26351c39d8d9d706ac1cc6/IccProfLib/IccCmm.cpp:1923:22: error: out-of-line definition of 'CIccApplyNDLutXform' does not match any declaration in 'CIccApplyNDLutXform'
DemoIccMAX-b90ac3933da99179df26351c39d8d9d706ac1cc6/IccProfLib/IccCmm.cpp:6306:33: error: no matching constructor for initialization of 'CIccApplyNDLutXform'
DemoIccMAX-b90ac3933da99179df26351c39d8d9d706ac1cc6/IccProfLib/IccCmm.cpp:6306:33: error: no matching constructor for initialization of 'CIccApplyNDLutXform'
DemoIccMAX-b90ac3933da99179df26351c39d8d9d706ac1cc6/Build/Cmake/../../IccProfLib/IccCmm.h:1246:23: error: unknown type name 'CIccXformNDLut'
Result: Not Passing
in IccProfLib/IccCmm.h:1828
Hi,
we included IccProfLib into one of our image validation tools. The tool calls CIccProfile::ReadValidate on embedded profiles.
We noticed that profiles validated with the IccDumpProfile CI Tool to be non-compliant, are still valid and compliant when tested with this IccProfLib call.
One real-world example we encountered: First tag data that is on an offset rather than immediately after tag table (as expected by the ICC standard).
It seems IccDumpProfile implements more non-compliant checks and warnings.
--> https://github.com/InternationalColorConsortium/DemoIccMAX/blob/master/Tools/CmdLine/IccDumpProfile/iccDumpProfile.cpp#L241-L304
Would it be possible to include those into IccProfLib as well to get consistent validation results and reports across all tools and libs in this project?
System:
Ubuntu 22,04.4, AMD_64
Commands:
gh repo clone InternationalColorConsortium/DemoIccMAX
cd ~/DemoIccMAX
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=$HOME/.local ../Build/Cmake
make help
make
Output from make:
[ 0%] Building CXX object IccProfLib/CMakeFiles/IccProfLib2.dir/home/ott/DemoIccMAX/IccProfLib/IccApplyBPC.cpp.o
In file included from /home/ott/DemoIccMAX/IccProfLib/IccApplyBPC.h:74,
from /home/ott/DemoIccMAX/IccProfLib/IccApplyBPC.cpp:71:
/home/ott/DemoIccMAX/IccProfLib/IccCmm.h:1246:37: error: expected ‘)’ before ‘*’ token
1246 | CIccApplyNDLutXform(CIccXformNDLut* pXform, CIccApplyCLUT* pApply);
| ~ ^
| )
make[2]: *** [IccProfLib/CMakeFiles/IccProfLib2.dir/build.make:76: IccProfLib/CMakeFiles/IccProfLib2.dir/home/ott/DemoIccMAX/IccProfLib/IccApplyBPC.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:381: IccProfLib/CMakeFiles/IccProfLib2.dir/all] Error 2
make: *** [Makefile:136: all] Error 2
When running "iccDumpProfile" CLI utility on PC or Linux with the Google SKIA file "profiles/fuzz/curv_size_overflow.icc" and either or both -v or all options, RefIccMax consumes excessive memory (multiple GBs) and is exceptionally slow as a result. wxDumpProfile on PC is much faster and does not seem to suffer this (but I did not check the code). Without -v or all options "iccDumpProfile" runs very quickly like wxDumpProfile.
As the ICC profile filename suggests, the root cause is that the RGB TRC 'curv' have counts (n) of 2,147,484,348 which then attempts to allocate gigantic buffers * sizeof(float)! This is because the ICC file has been fuzzed and the corresponding bytes for count in the file are all 0xFF... (so, yes, it is malformed).
Problematic code is in IccProfLib/IccTagLut.cpp, function bool CIccTagCurve::SetSize(...) from line 442 (https://github.com/InternationalColorConsortium/RefIccMAX/blob/8da81e9151581161a582dbc4ca0ed8220aba02a9/IccProfLib/IccTagLut.cpp#L442). Would suggest that santity checks on the magnitude of count (n) prior to doing malloc/icRealloc are required. There may even be a maximum possible value for count given that the data for 'curv' are all uInt16Numbers according to ICC.1 v4.3.0 and the curves may have some precision requirements but all that color theory is beyond me...
Also suggest checking that all other malloc/icRealloc calls in the codebase are also range-checked prior to calls.
To get the Google SKIA ICC corpus and skcms/profiles/fuzz/curv_size_overflow.icc:
git clone https://skia.googlesource.com/skcms ## quite small repo (~180MB), with corpus in 'profiles' folder and below
cd skcms/profiles/fuzz
In RefIccMax-2.0.16:
A couple of minor issues with the mark-up in the xml examples in the Testing folder:
In several 'camera' examples including CameraModel.xml:
... is closed with instead of
is closed with
When these are corrected, there is a stack underflow at line 72: [mul] 33
PR will be based on Commit: f891074
Description: The code in CIccTagSpectralViewingConditions and CIccProfile exhibits improper memory management practices.
There is a mismatch in allocation and deallocation methods for dynamically allocated memory.
This leads to the detection of alloc-dealloc-mismatch errors by AddressSanitizer.
File: IccTagBasic.cpp
Line: 10908
Incorrect use of delete[] for memory allocated using malloc.
CIccProfile Cleanup Function:
The provided code segment shows the allocation of m_observer using malloc:
if (pChild->children && pChild->children->content) {
CIccFloatArray vals;
vals.ParseTextArray((icChar*)pChild->children->content);
if (vals.GetSize() != m_observerRange.steps * 3)
return false;
m_observer = (icFloatNumber*)malloc(m_observerRange.steps * 3 * sizeof(icFloatNumber));
if (!m_observer)
return false;
icFloatNumber *pBuf = vals.GetBuf();
memcpy(m_observer, pBuf, m_observerRange.steps * 3 * sizeof(icFloatNumber));
}
Given that m_observer is allocated using malloc, it should be deallocated using free in the destructor.
Here is the corrected destructor code:
/**
****************************************************************************
* Name: CIccTagSpectralViewingConditions::~CIccTagSpectralViewingConditions
*
* Purpose: Destructor
*
*****************************************************************************
*/
CIccTagSpectralViewingConditions::~CIccTagSpectralViewingConditions()
{
if (m_observer)
free(m_observer); // Correct deallocation for malloc
if (m_illuminant)
free(m_illuminant); // Correct deallocation for malloc
}
Additional Considerations
Ensure All Allocations Match: Verify that m_illuminant is also allocated using malloc. If not, adjust the deallocation method accordingly.
Memory Management Consistency: Ensure that all memory allocations and deallocations within the class are consistent to prevent similar issues.
Error Handling: Consider adding error handling to ensure that the destructor handles null pointers gracefully, although free and delete[] are safe to call with null pointers.
File: IccProfile.cpp
Line: 263
Improper handling of dynamically allocated memory using manual delete and free operations.
/**
****************************************************************************
* Name: CIccProfile::Cleanup
*
* Purpose: Detach from a pending IO object
*****************************************************************************
*/
void CIccProfile::Cleanup()
{
// Delete the attached IO object if it exists
if (m_pAttachIO) {
delete m_pAttachIO;
m_pAttachIO = nullptr;
}
// Iterate through the tag values and delete them if they are not null
for (auto &tagVal : *m_TagVals) {
if (tagVal.ptr != nullptr) {
delete tagVal.ptr;
tagVal.ptr = nullptr;
}
}
// Clear the tag lists
m_Tags->clear();
m_TagVals->clear();
// Reset the header to zero
std::memset(&m_Header, 0, sizeof(m_Header));
}
Nullptr Assignment: Replaced NULL with nullptr for better type safety and clarity. After deleting pointers, set them to nullptr to avoid dangling pointers.
Range-based Loop: Replaced the manual iterator loop with a range-based loop for better readability and to reduce the potential for errors.
Clear Lists: Clear the tag lists using the clear method of the standard library containers.
Memset: Used std::memset instead of memset to adhere to C++ standards.
Memory Allocation Consistency: Ensure that the memory for m_pAttachIO and tagVal.ptr is consistently allocated with new if they are deleted with delete.
CWE-762: Mismatched Memory Management Routines
CWE-401: Improper Release of Memory Before Removing Last Reference ('Memory Leak')
Crashes occurring in destructors due to zero-page dereferences are typically considered benign and are often just null pointer dereferences without significant security implications. However, crashes occurring in constructors are more concerning as they may indicate a product defect or a security vulnerability.
Crash on write instruction
Crash executing invalid address
Crash calling an invalid address
Illegal instruction exception
Abort due to -fstack-protector, _FORTIFY_SOURCE, heap corruption detected
Stack trace of crashing thread contains certain functions such as malloc, free, szone_error, objc_MsgSend, etc.
Divide by zero exception
Stack grows too large due to recursion
Null dereference (read or write)
Other abort
Crash on read instruction
A reference implementation is not production code. In general, any Code being used in production should undergo additional security reviews and product testing.
While attempting to use iccToXml
and iccFromXml
to create a profile for "A CMYK (four channel)" to "B Lab (three channel)" the tool segfaulted. The issue is a confusion about properly keeping track of which of A and B are the input and the output. The "AtoB" and "BtoA" types do this fairly well, but the "Lut8" and "Lut16" types currently appear to always assume B is the input. This kinda works in a somewhat confusing way, but only when A and B have the same number of channels.
By creating a hack like bungeman@a963b3b (originally against sample-icc and iccxml-code because that's what I found first) I am able to create and round trip a simple CMYK profile like cmyk2x2.zip (zipped since I cannot attach a bare icc file here). Since this change isn't very clean at the moment I've decided to open this as an issue instead of as a pull request. Note that the messy part of this change is around informing the Lut classes of their direction, the changes to the classes to output correctly when they have the proper direction set seems to be correct.
Instead of maintaining separate platform specific projects, create a single CMake
since two days I am trying to compile the project with no success unfortunately.
I have followed whatever instructions were mentioned on the home page of the project for Windows OS (e.g. setting up all the necessary system variables LIBXML2, LIBTIFF...etc.) and yet it doesn't work!
I needed however actually to include the libraries explicitly in all the projects in visual studio solution and still it doesn't work.
Clearer instructions would be appreciated on how to make it runs.
The attached ICC file (generated by fuzzing engine) generates a heap validation failure when doing "-v double-free.icc all" with iccDumpProfile on an MSVC-2019 Windows 10 x64 Debug build. We think it is a double-free. This was issue was identified using AFL-cmin fuzzing.
I noticed that when I apply the profile below to CMM, "-nan(ind)" may be returned.
InputProfile : ICCProfile_sRGB_v5.xml.icc
OutputProfile : ICCProfile_AdobeRGB_v5.xml.icc
For example, if you input a RGB device value = ( 0, 0, 64) (normalized RGB value = ( 0, 0, 0.2510),
the output will be a normalized RGB value = ( -nan(ind), -nan(ind), 0.2462).
This is because x is slightly negative in the calculation of pow (float x, float y) in the following part, probably because of a calculation error, and it is out of the definition range.
DemoIccMAX/IccProfLib/IccMpeBasic.cpp
Lines 498 to 499 in 79ecb74
Before calculating the pow, I think it might be a good idea to include a process such as rounding the range of x to 0-1.
Alternatively, improvements may be needed elsewhere.
I attached the sample code and profiles.
TestSample.zip
Thank you for your contribution.
I can't get the CMake or XCode builds to work properly on MacOS and produce scripts that are portable to other Macs. Any advice on this would be much appreciated.
My system is:
The closest build command I've figured out is:
brew install libtiff
mkdir dist/ && cd dist/ && cmake -DENABLE_STATIC_LIBS=ON -DENABLE_SHARED_LIBS=OFF ../Build/Cmake && make && make install
This produces a valid build for my machine, but the scripts have a link to libtiff
- as installed by homebrew, libtiff
doesn't come installed on MacOS by default. So this script is does not work on other people's machines.
$ otool -L Tools/IccApplyProfiles/iccApplyProfiles
/usr/local/opt/libtiff/lib/libtiff.5.dylib (compatibility version 11.0.0, current version 11.0.0)
/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 164.0.0)
/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit (compatibility version 1.0.0, current version 275.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 904.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.0.0)
Is there a way to build iccApplyProfiles
on MacOS so that it can be used on other Macs? I think this might be called "relocatable packages" and involve including libtiff
within the builds somehow - but I'm not sure. I haven't been able to figure it out.
I also tried building using XCode, but it did not work.
after copying the required files, as per the README:
$ cd Build/XCode && ./BuildAll.sh
Command line invocation:
/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -target IccProfLib-macOS -configuration Release
note: Using new build system
note: Planning build
note: Using build description from disk
** BUILD SUCCEEDED **
cp: Build/Release/usr is a directory (not copied).
Command line invocation:
/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -target IccLibXML-macOS -configuration Release
note: Using new build system
note: Planning build
note: Using build description from disk
** BUILD SUCCEEDED **
cp: build/Release/usr is a directory (not copied).
Command line invocation:
/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -target IccApplyNamedCMM -configuration Release
note: Using new build system
note: Planning build
note: Using build description from disk
error: The linked library 'libIccProfLib.a' is missing one or more architectures required by this target: arm64. (in target 'IccApplyNamedCMM' from project 'IccApplyNamedCMM')
** BUILD FAILED **
cp: build/Release/IccApplyNamedCmm: No such file or directory
...
In IccCmm.cpp
, inside the methods CIccCmm::ToInternalEncoding
(line 8045
) and CIccCmm::FromInternalEncoding
(line 8354
), an encoding of 8 bits while using the XYZ color space is currently not implemented, I was wondering whether this encoding is possible with the XYZ space or not?
Thanks
Running BuildAll.sh on OS X fails because project contains fixed paths
ex.
clang: error: no such file or directory: '/Development/ICC/ICCMax/RefIccMAX-2.0.17/IccXML/IccLibXML/IccMpeXml.cpp'
The examples contain 'smwp', like here:
https://github.com/InternationalColorConsortium/RefIccMAX/blob/master/Testing/Display/LCDDisplay.xml#L540
... translates maybe to Spectral Media White Point
The spec says in 9.2.106 spectralWhitePointTag
Tag signature: ‘swpt’ (73777074h)
Do I miss something? Which one is correct?
It would be extremely handy if all the CLI tools reported the version of the underlying IccProfLib as otherwise there is no known way to identify versioning information across multiple instances or from log files. Compile dates are obviously unreliable.
wxProfileDump already has the equivalent in the Help | About dialog.
The simplistic way is adding 2 lines of code to each CLI main program:
#include "IccProfLibVer.h"
...
printf("Built with IccProfLib version " ICCPROFLIBVER "\n");
Or you may prefer to implement as a method.
If you let me know your preference I am happy to make said changes... or you can do.
When a gamutBoundaryDescType tag is created in XML, only values from the first two vertices seem to be encoded in the profile by IccFromXml. After the first device value for the second vertex, the remainder of the data are encoded as zeros.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.