diff --git a/SECURITY.md b/SECURITY.md index 19555844b9..2693a4a8da 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -14,20 +14,16 @@ would be naive to say our code is immune to every exploit. ## Reporting Vulnerabilities -Quickly resolving security related issues is a priority. If you think -you've found a potential vulnerability in OpenColorIO, please report it by -emailing security@opencolorio.org. Only TSC members and ASWF project -management have access to these messages. +Quickly resolving security related issues is a priority. The best way to report a +vulnerability is to file a GitHub security advisory. If that is not possible, it +is also fine to email your report to security@opencolorio.org. Only the project +administrators have access to these reports. Include detailed steps to reproduce the issue, and any other information that could aid an investigation. Someone will assess the report and make every effort to respond within 14 days. -## Outstanding Security Issues - -None - -## Addressed Security Issues +## History of CVE Fixes None @@ -64,6 +60,3 @@ set of behaviors as with file loading. It is a bug if calling a function with well-formed arguments causes the library to crash. It is a security issue if calling a function with well-formed arguments causes arbitrary code execution. - -We do not consider this as severe as file format issues because in most -deployments the parameter space is not exposed to potential attackers. diff --git a/docs/site/homepage/config.toml b/docs/site/homepage/config.toml index 7f7e82d351..a7e4dfc865 100644 --- a/docs/site/homepage/config.toml +++ b/docs/site/homepage/config.toml @@ -105,14 +105,6 @@ post_share = true enable = false preloader = "images/opencolorio-color.png" -# google map -[params.map] -enable = false -gmap_api = "https://maps.googleapis.com/maps/api/js?key=AIzaSyBu5nZKbeK-WHQ70oqOWo-_4VmwOwKP9YQ" -map_latitude = "51.5223477" -map_longitude = "-0.1622023" -map_marker = "images/marker.png" - ############################# ASWF LINKS ########################## [[params.aswf]] diff --git a/ext/sampleicc/src/include/iccProfileReader.h b/ext/sampleicc/src/include/iccProfileReader.h index 8b28753c54..9abce18b33 100644 --- a/ext/sampleicc/src/include/iccProfileReader.h +++ b/ext/sampleicc/src/include/iccProfileReader.h @@ -586,6 +586,10 @@ namespace SampleICC if (!Read32(istream, &sizeData, 1)) return false; + // ICC curve entries are indexed by 16-bit values; 65536 is the maximum. + if (sizeData > 65536) + return false; + mCurve.resize(sizeData); if (sizeData) diff --git a/src/OpenColorIO/ColorSpace.cpp b/src/OpenColorIO/ColorSpace.cpp index 98f776fe18..946c7286c1 100644 --- a/src/OpenColorIO/ColorSpace.cpp +++ b/src/OpenColorIO/ColorSpace.cpp @@ -162,6 +162,7 @@ const char * ColorSpace::getAlias(size_t idx) const noexcept bool ColorSpace::hasAlias(const char * alias) const noexcept { + if (!alias) return false; for (size_t idx = 0; idx < getImpl()->m_aliases.size(); ++idx) { if (0 == Platform::Strcasecmp(getImpl()->m_aliases[idx].c_str(), alias)) @@ -430,6 +431,7 @@ int ColorSpace::getAllocationNumVars() const void ColorSpace::getAllocationVars(float * vars) const { + if(!vars) return; if(!getImpl()->m_allocationVars.empty()) { memcpy(vars, @@ -440,6 +442,15 @@ void ColorSpace::getAllocationVars(float * vars) const void ColorSpace::setAllocationVars(int numvars, const float * vars) { + if (numvars < 0) + { + throw Exception("setAllocationVars: numvars must not be negative."); + } + if (numvars > 0 && !vars) + { + throw Exception("setAllocationVars: vars must not be null when numvars is positive."); + } + getImpl()->m_allocationVars.resize(numvars); if(!getImpl()->m_allocationVars.empty()) diff --git a/src/OpenColorIO/ColorSpaceSet.cpp b/src/OpenColorIO/ColorSpaceSet.cpp index 99c659a61b..488099be68 100644 --- a/src/OpenColorIO/ColorSpaceSet.cpp +++ b/src/OpenColorIO/ColorSpaceSet.cpp @@ -180,6 +180,7 @@ class ColorSpaceSet::Impl void remove(const char * csName) { + if (!csName || !*csName) return; const std::string name = StringUtils::Lower(csName); if (name.empty()) return; diff --git a/src/OpenColorIO/Config.cpp b/src/OpenColorIO/Config.cpp index d4d5fbe7fa..1b5a8c70db 100644 --- a/src/OpenColorIO/Config.cpp +++ b/src/OpenColorIO/Config.cpp @@ -4337,7 +4337,7 @@ int Config::getNumDisplaysAll() const noexcept const char * Config::getDisplayAll(int index) const noexcept { - if (index >= 0 || index < static_cast(getImpl()->m_displays.size())) + if (index >= 0 && index < static_cast(getImpl()->m_displays.size())) { return getImpl()->m_displays[index].first.c_str(); } @@ -4365,7 +4365,7 @@ int Config::getDisplayAllByName(const char * name) const noexcept bool Config::isDisplayTemporary(int index) const noexcept { - if (index >= 0 || index < static_cast(getImpl()->m_displays.size())) + if (index >= 0 && index < static_cast(getImpl()->m_displays.size())) { return getImpl()->m_displays[index].second.m_temporary; } @@ -4375,7 +4375,7 @@ bool Config::isDisplayTemporary(int index) const noexcept void Config::setDisplayTemporary(int index, bool isTemporary) noexcept { - if (index >= 0 || index < static_cast(getImpl()->m_displays.size())) + if (index >= 0 && index < static_cast(getImpl()->m_displays.size())) { getImpl()->m_displays[index].second.m_temporary = isTemporary; @@ -4410,7 +4410,7 @@ const char * Config::getView(ViewType type, const char * display, int index) con { if (!display || !*display) { - if (index >= 0 || index < static_cast(getImpl()->m_sharedViews.size())) + if (index >= 0 && index < static_cast(getImpl()->m_sharedViews.size())) { return getImpl()->m_sharedViews[index].m_name.c_str(); } @@ -4449,11 +4449,19 @@ const char * Config::getView(ViewType type, const char * display, int index) con void Config::getDefaultLumaCoefs(double * c3) const { + if (!c3) + { + throw Exception("getDefaultLumaCoefs: c3 must not be null."); + } memcpy(c3, &getImpl()->m_defaultLumaCoefs[0], 3*sizeof(double)); } void Config::setDefaultLumaCoefs(const double * c3) { + if (!c3) + { + throw Exception("setDefaultLumaCoefs: c3 must not be null."); + } memcpy(&getImpl()->m_defaultLumaCoefs[0], c3, 3*sizeof(double)); AutoMutex lock(getImpl()->m_cacheidMutex); diff --git a/src/OpenColorIO/ContextVariableUtils.cpp b/src/OpenColorIO/ContextVariableUtils.cpp index 1207a0363a..82726d4234 100644 --- a/src/OpenColorIO/ContextVariableUtils.cpp +++ b/src/OpenColorIO/ContextVariableUtils.cpp @@ -101,10 +101,12 @@ void LoadEnvironment(EnvMap & map, bool update) const std::string env_str = (char*)*env; #endif - const int pos = static_cast(env_str.find_first_of('=')); + const auto pos = env_str.find_first_of('='); + + if (pos == std::string::npos) continue; const std::string name = env_str.substr(0, pos); - const std::string value = env_str.substr(pos+1, env_str.length()); + const std::string value = env_str.substr(pos+1); if (update) { @@ -122,8 +124,12 @@ void LoadEnvironment(EnvMap & map, bool update) } } -std::string ResolveContextVariables(const std::string & str, const EnvMap & map, UsedEnvs & used) +static std::string ResolveContextVariablesImpl(const std::string & str, const EnvMap & map, + UsedEnvs & used, int depth) { + // Guard against infinite recursion from cyclic variable references. + if (depth > 32) return str; + // Early exit if no reserved tokens are found. if (!ContainsContextVariables(str)) { @@ -159,12 +165,17 @@ std::string ResolveContextVariables(const std::string & str, const EnvMap & map, // recursively call till string doesn't expand anymore if(newstr != orig) { - return ResolveContextVariables(newstr, map, used); + return ResolveContextVariablesImpl(newstr, map, used, depth + 1); } return orig; } +std::string ResolveContextVariables(const std::string & str, const EnvMap & map, UsedEnvs & used) +{ + return ResolveContextVariablesImpl(str, map, used, 0); +} + bool CollectContextVariables(const Config & config, const Context & context, ConstTransformRcPtr transform, diff --git a/src/OpenColorIO/CustomKeys.h b/src/OpenColorIO/CustomKeys.h index b41b9714ce..02f364bc1f 100644 --- a/src/OpenColorIO/CustomKeys.h +++ b/src/OpenColorIO/CustomKeys.h @@ -61,14 +61,24 @@ class CustomKeysContainer bool hasKey(const char * key) { - std::string s = key; - return m_customKeys.count(s) > 0; + if (!key || !*key) return false; + return m_customKeys.count(key) > 0; } const char * getValueForKey(const char * key) { - // NB: Will throw if the map doesn't have the key. - return m_customKeys[key].c_str(); + if (!key || !*key) + { + throw Exception("Key has to be a non-empty string."); + } + auto it = m_customKeys.find(key); + if (it == m_customKeys.end()) + { + std::ostringstream oss; + oss << "Key '" << key << "' not found."; + throw Exception(oss.str().c_str()); + } + return it->second.c_str(); } private: diff --git a/src/OpenColorIO/Display.cpp b/src/OpenColorIO/Display.cpp index 4280b99231..aebc37fb08 100644 --- a/src/OpenColorIO/Display.cpp +++ b/src/OpenColorIO/Display.cpp @@ -52,7 +52,7 @@ void AddView(ViewVec & views, const char * name, const char * viewTransform, const char * displayColorSpace, const char * looks, const char * rule, const char * description) { - if (0 == Platform::Strcasecmp(displayColorSpace, OCIO_VIEW_USE_DISPLAY_NAME)) + if (displayColorSpace && 0 == Platform::Strcasecmp(displayColorSpace, OCIO_VIEW_USE_DISPLAY_NAME)) { displayColorSpace = OCIO_VIEW_USE_DISPLAY_NAME; } diff --git a/src/OpenColorIO/Display.h b/src/OpenColorIO/Display.h index 43e1b2cc9c..b2ffd85b50 100644 --- a/src/OpenColorIO/Display.h +++ b/src/OpenColorIO/Display.h @@ -37,7 +37,7 @@ struct View const char * looks, const char * rule, const char * description) - : m_name(name) + : m_name(name ? name : "") , m_viewTransform(viewTransform ? viewTransform : "") , m_colorspace(colorspace ? colorspace : "") , m_looks(looks ? looks : "") diff --git a/src/OpenColorIO/DynamicProperty.cpp b/src/OpenColorIO/DynamicProperty.cpp index 075973412f..bf61a484cd 100644 --- a/src/OpenColorIO/DynamicProperty.cpp +++ b/src/OpenColorIO/DynamicProperty.cpp @@ -280,6 +280,10 @@ void DynamicPropertyGradingRGBCurveImpl::precompute() { ConstGradingBSplineCurveRcPtr curve = m_gradingRGBCurve->getCurve(c); auto curveImpl = dynamic_cast(curve.get()); + if (!curveImpl) + { + throw Exception("DynamicPropertyGradingRGBCurveImpl: unexpected curve implementation."); + } curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c), false); } if (m_knotsCoefs.m_numKnots <= 0) m_knotsCoefs.m_localBypass = true; @@ -375,6 +379,10 @@ void DynamicPropertyGradingHueCurveImpl::precompute() { ConstGradingBSplineCurveRcPtr curve = m_gradingHueCurve->getCurve(c); auto curveImpl = dynamic_cast(curve.get()); + if (!curveImpl) + { + throw Exception("DynamicPropertyGradingHueCurveImpl: unexpected curve implementation."); + } curveImpl->computeKnotsAndCoefs(m_knotsCoefs, static_cast(c), m_gradingHueCurve->getDrawCurveOnly()); } diff --git a/src/OpenColorIO/GpuShader.cpp b/src/OpenColorIO/GpuShader.cpp index 665cee02f1..95d2572750 100644 --- a/src/OpenColorIO/GpuShader.cpp +++ b/src/OpenColorIO/GpuShader.cpp @@ -11,7 +11,7 @@ #include "DynamicProperty.h" #include "GpuShader.h" -#include "ops/lut3d/Lut3DOpData.h" +#include "LutLimits.h" #include "Platform.h" namespace OCIO_NAMESPACE @@ -194,7 +194,7 @@ class PrivateImpl virtual ~PrivateImpl() {} - inline unsigned get3dLutMaxLength() const { return Lut3DOpData::maxSupportedLength; } + inline unsigned get3dLutMaxLength() const { return Max3DLUTLength; } inline unsigned get1dLutMaxWidth() const { return m_max1DLUTWidth; } inline void set1dLutMaxWidth(unsigned maxWidth) { m_max1DLUTWidth = maxWidth; } diff --git a/src/OpenColorIO/GpuShaderDesc.cpp b/src/OpenColorIO/GpuShaderDesc.cpp index 574a7c3594..c16add78ad 100644 --- a/src/OpenColorIO/GpuShaderDesc.cpp +++ b/src/OpenColorIO/GpuShaderDesc.cpp @@ -135,7 +135,7 @@ void GpuShaderCreator::setFunctionName(const char * name) noexcept { AutoMutex lock(getImpl()->m_cacheIDMutex); // Note: Remove potentially problematic double underscores from GLSL resource names. - getImpl()->m_functionName = StringUtils::Replace(name, "__", "_"); + getImpl()->m_functionName = StringUtils::Replace(name ? name : "", "__", "_"); getImpl()->m_cacheID.clear(); } @@ -148,7 +148,7 @@ void GpuShaderCreator::setResourcePrefix(const char * prefix) noexcept { AutoMutex lock(getImpl()->m_cacheIDMutex); // Note: Remove potentially problematic double underscores from GLSL resource names. - getImpl()->m_resourcePrefix = StringUtils::Replace(prefix, "__", "_"); + getImpl()->m_resourcePrefix = StringUtils::Replace(prefix ? prefix : "", "__", "_"); getImpl()->m_cacheID.clear(); } @@ -161,7 +161,7 @@ void GpuShaderCreator::setPixelName(const char * name) noexcept { AutoMutex lock(getImpl()->m_cacheIDMutex); // Note: Remove potentially problematic double underscores from GLSL resource names. - getImpl()->m_pixelName = StringUtils::Replace(name, "__", "_"); + getImpl()->m_pixelName = StringUtils::Replace(name ? name : "", "__", "_"); getImpl()->m_cacheID.clear(); } diff --git a/src/OpenColorIO/HashUtils.cpp b/src/OpenColorIO/HashUtils.cpp index 627ab20661..c5d6035013 100644 --- a/src/OpenColorIO/HashUtils.cpp +++ b/src/OpenColorIO/HashUtils.cpp @@ -22,7 +22,9 @@ std::string CacheIDHash(const char * array, std::size_t size) XXH128_hash_t hash = XXH3_128bits(array, size); std::stringstream oss; - oss << std::hex << hash.low64 << hash.high64; + oss << std::hex << std::setfill('0'); + oss << std::setw(16) << hash.low64; + oss << std::setw(16) << hash.high64; return oss.str(); } diff --git a/src/OpenColorIO/ImageDesc.cpp b/src/OpenColorIO/ImageDesc.cpp index 2f045f1597..7830168987 100644 --- a/src/OpenColorIO/ImageDesc.cpp +++ b/src/OpenColorIO/ImageDesc.cpp @@ -260,6 +260,7 @@ struct PackedImageDesc::Impl { // Confirm xStrideBytes is a pure packing // (I.e., it will divide evenly) + if (m_chanStrideBytes == 0) return false; const div_t result = div((int)m_xStrideBytes, (int)m_chanStrideBytes); if(result.rem != 0) return false; diff --git a/src/OpenColorIO/Logging.cpp b/src/OpenColorIO/Logging.cpp index e40692db7c..4331878dfe 100644 --- a/src/OpenColorIO/Logging.cpp +++ b/src/OpenColorIO/Logging.cpp @@ -114,11 +114,17 @@ void SetLoggingLevel(LoggingLevel level) void SetLoggingFunction(LoggingFunction logFunction) { + if (!logFunction) + { + throw Exception("SetLoggingFunction: logFunction must not be null."); + } + AutoMutex lock(g_logmutex); g_loggingFunction = logFunction; } void ResetToDefaultLoggingFunction() { + AutoMutex lock(g_logmutex); g_loggingFunction = DefaultLoggingFunction; } diff --git a/src/OpenColorIO/LutLimits.h b/src/OpenColorIO/LutLimits.h new file mode 100644 index 0000000000..d7ce24ccc7 --- /dev/null +++ b/src/OpenColorIO/LutLimits.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenColorIO Project. + +#ifndef INCLUDED_OCIO_LUTLIMITS_H +#define INCLUDED_OCIO_LUTLIMITS_H + +namespace OCIO_NAMESPACE +{ + +// Maximum number of entries supported in a 1D LUT. +constexpr unsigned long Max1DLUTLength = 300000; + +// Maximum grid size supported for a 3D LUT. +// 129 allows for a MESH dimension of 7 in the 3dl file format. +constexpr unsigned long Max3DLUTLength = 129; + +} // namespace OCIO_NAMESPACE + +#endif // INCLUDED_OCIO_LUTLIMITS_H diff --git a/src/OpenColorIO/NamedTransform.cpp b/src/OpenColorIO/NamedTransform.cpp index 2eb8f53b19..19c6d8d67c 100644 --- a/src/OpenColorIO/NamedTransform.cpp +++ b/src/OpenColorIO/NamedTransform.cpp @@ -67,6 +67,7 @@ const char * NamedTransformImpl::getAlias(size_t idx) const noexcept bool NamedTransformImpl::hasAlias(const char * alias) const noexcept { + if (!alias || !*alias) return false; for (size_t idx = 0; idx < m_aliases.size(); ++idx) { if (0 == Platform::Strcasecmp(m_aliases[idx].c_str(), alias)) diff --git a/src/OpenColorIO/OCIOYaml.cpp b/src/OpenColorIO/OCIOYaml.cpp index c9361579fe..9010dcb606 100644 --- a/src/OpenColorIO/OCIOYaml.cpp +++ b/src/OpenColorIO/OCIOYaml.cpp @@ -4413,17 +4413,24 @@ inline void load(const YAML::Node& node, ConfigRcPtr & config, const char* filen results = StringUtils::Split(version, '.'); - if(results.size()==1) - { - profile_major_version = std::stoi(results[0].c_str()); - profile_minor_version = 0; - } - else if(results.size()==2) + try { - profile_major_version = std::stoi(results[0].c_str()); - profile_minor_version = std::stoi(results[1].c_str()); + if(results.size()==1) + { + profile_major_version = std::stoi(results[0].c_str()); + profile_minor_version = 0; + } + else if(results.size()==2) + { + profile_major_version = std::stoi(results[0].c_str()); + profile_minor_version = std::stoi(results[1].c_str()); + } + else + { + faulty_version = true; + } } - else + catch (const std::exception &) { faulty_version = true; } diff --git a/src/OpenColorIO/OCIOZArchive.cpp b/src/OpenColorIO/OCIOZArchive.cpp index 982fce6823..1caed9ecba 100644 --- a/src/OpenColorIO/OCIOZArchive.cpp +++ b/src/OpenColorIO/OCIOZArchive.cpp @@ -387,6 +387,21 @@ void ExtractOCIOZArchive(const char * archivePath, const char * destination) mz_zip_reader_delete(&extracter); } +std::vector readEntryBuffer(void * reader) +{ + int32_t buf_size = (int32_t)mz_zip_reader_entry_save_buffer_length(reader); + // Reject negative values (minizip error codes) and implausibly large entries. + // 256 MB is well above any realistic individual OCIO config or LUT file size. + static constexpr size_t MAX_ENTRY_SIZE = 256 * 1024 * 1024; + if (buf_size <= 0 || (size_t)buf_size > MAX_ENTRY_SIZE) + { + throw Exception("OCIOZ archive entry size is invalid or exceeds maximum allowed size."); + } + std::vector buffer(buf_size); + mz_zip_reader_entry_save_buffer(reader, &buffer[0], buf_size); + return buffer; +} + /** * \brief Callback function for getFileStringFromArchiveStream in order to get the contents of a * file inside an OCIOZ archive as a buffer. @@ -406,11 +421,7 @@ std::vector getFileBufferByPath(void * reader, mz_zip_file & info, std: std::vector buffer; if (mz_path_compare_wc(filepath.c_str(), info.filename, 1) == MZ_OK) { - // Initialize the buffer for the file. - int32_t buf_size = (int32_t)mz_zip_reader_entry_save_buffer_length(reader); - buffer.resize(buf_size); - // Read the content of the file and return it as buffer. - mz_zip_reader_entry_save_buffer(reader, &buffer[0], buf_size); + buffer = readEntryBuffer(reader); } return buffer; } @@ -434,9 +445,7 @@ std::vector getFileBufferByExtension(void * reader, mz_zip_file & info, pystring::os::path::splitext(root, ext, info.filename); if (Platform::Strcasecmp(extension.c_str(), ext.c_str()) == 0) { - int32_t buf_size = (int32_t)mz_zip_reader_entry_save_buffer_length(reader); - buffer.resize(buf_size); - mz_zip_reader_entry_save_buffer(reader, &buffer[0], buf_size); + buffer = readEntryBuffer(reader); } return buffer; } diff --git a/src/OpenColorIO/Platform.cpp b/src/OpenColorIO/Platform.cpp index 0e7fc68b2d..f579c49bdd 100644 --- a/src/OpenColorIO/Platform.cpp +++ b/src/OpenColorIO/Platform.cpp @@ -22,7 +22,7 @@ namespace OCIO_NAMESPACE const char * GetEnvVariable(const char * name) { - static std::string value; + thread_local std::string value; Platform::Getenv(name, value); return value.c_str(); } @@ -183,12 +183,16 @@ int Strncasecmp(const char * str1, const char * str2, size_t n) void * AlignedMalloc(size_t size, size_t alignment) { #ifdef _WIN32 - return _aligned_malloc(size, alignment); + void * memBlock = _aligned_malloc(size, alignment); #else - void* memBlock = 0x0; - if (!posix_memalign(&memBlock, alignment, size)) return memBlock; - return 0x0; + void* memBlock = nullptr; + if (posix_memalign(&memBlock, alignment, size)) memBlock = nullptr; #endif + if (!memBlock) + { + throw Exception("AlignedMalloc: memory allocation failure."); + } + return memBlock; } void AlignedFree(void* memBlock) diff --git a/src/OpenColorIO/Processor.cpp b/src/OpenColorIO/Processor.cpp index 83b8468b75..992701855f 100755 --- a/src/OpenColorIO/Processor.cpp +++ b/src/OpenColorIO/Processor.cpp @@ -289,6 +289,10 @@ int Processor::Impl::getNumTransforms() const const FormatMetadata & Processor::Impl::getTransformFormatMetadata(int index) const { + if (index < 0 || index >= static_cast(m_ops.size())) + { + throw Exception("Processor::getTransformFormatMetadata: index out of range."); + } auto op = OCIO_DYNAMIC_POINTER_CAST(m_ops[index]); return op->data()->getFormatMetadata(); } @@ -346,8 +350,19 @@ OptimizationFlags EnvironmentOverride(OptimizationFlags oFlags) const std::string envFlag = GetEnvVariable(OCIO_OPTIMIZATION_FLAGS_ENVVAR); if (!envFlag.empty()) { - // Use 0 to allow base to be determined by the format. - oFlags = static_cast(std::stoul(envFlag, nullptr, 0)); + try + { + // Use 0 to allow base to be determined by the format. + oFlags = static_cast(std::stoul(envFlag, nullptr, 0)); + } + catch (const std::exception & e) + { + std::string msg("Illegal value for "); + msg += OCIO_OPTIMIZATION_FLAGS_ENVVAR; + msg += ": "; + msg += e.what(); + throw Exception(msg.c_str()); + } } return oFlags; } diff --git a/src/OpenColorIO/SystemMonitor.cpp b/src/OpenColorIO/SystemMonitor.cpp index 8ee5e915e5..7fcc9c3663 100644 --- a/src/OpenColorIO/SystemMonitor.cpp +++ b/src/OpenColorIO/SystemMonitor.cpp @@ -8,6 +8,7 @@ #include +#include "Logging.h" #include "Mutex.h" #include "SystemMonitor.h" @@ -62,7 +63,14 @@ ConstSystemMonitorsRcPtr SystemMonitors::Get() noexcept if (!monitors) { SystemMonitorsRcPtr m = std::make_shared(); - DynamicPtrCast(m)->getAllMonitors(); + try + { + DynamicPtrCast(m)->getAllMonitors(); + } + catch (const std::exception & ex) + { + LogDebug(ex.what()); + } monitors = m; } diff --git a/src/OpenColorIO/TokensManager.h b/src/OpenColorIO/TokensManager.h index 706487289e..b2f02f3b08 100644 --- a/src/OpenColorIO/TokensManager.h +++ b/src/OpenColorIO/TokensManager.h @@ -51,6 +51,7 @@ class TokensManager void addToken(const char * token) { + if (!token || !*token) return; if (findToken(token) == m_tokens.end()) { m_tokens.push_back(StringUtils::Trim(token)); diff --git a/src/OpenColorIO/Transform.cpp b/src/OpenColorIO/Transform.cpp index 74223096fa..8b2b880699 100755 --- a/src/OpenColorIO/Transform.cpp +++ b/src/OpenColorIO/Transform.cpp @@ -373,9 +373,10 @@ void CreateTransform(GroupTransformRcPtr & group, ConstOpRcPtr & op) } else { + const auto & ref = *op; std::ostringstream error; error << "CreateTransform from op. Missing implementation for: " - << typeid(op).name(); + << typeid(ref).name(); throw Exception(error.str().c_str()); } diff --git a/src/OpenColorIO/apphelpers/CategoryHelpers.cpp b/src/OpenColorIO/apphelpers/CategoryHelpers.cpp index 33aa2ade2c..08a2249b6f 100644 --- a/src/OpenColorIO/apphelpers/CategoryHelpers.cpp +++ b/src/OpenColorIO/apphelpers/CategoryHelpers.cpp @@ -262,6 +262,7 @@ T Intersection(const T & list0, const T & list1) StringUtils::StringVec ExtractItems(const char * strings) { + if (!strings) return {}; StringUtils::StringVec tmp = StringUtils::Split(StringUtils::Lower(strings), ','); StringUtils::StringVec all; diff --git a/src/OpenColorIO/apphelpers/DisplayViewHelpers.cpp b/src/OpenColorIO/apphelpers/DisplayViewHelpers.cpp index d4055f46c1..4d3aa649e3 100644 --- a/src/OpenColorIO/apphelpers/DisplayViewHelpers.cpp +++ b/src/OpenColorIO/apphelpers/DisplayViewHelpers.cpp @@ -406,7 +406,7 @@ void AddDisplayView(ConfigRcPtr & config, { std::string errMsg; errMsg += "Connection color space name '"; - errMsg += connectionColorSpaceName; + errMsg += connectionColorSpaceName ? connectionColorSpaceName : "(null)"; errMsg += "' does not exist."; throw Exception(errMsg.c_str()); @@ -502,6 +502,8 @@ void AddDisplayView(ConfigRcPtr & config, void RemoveDisplayView(ConfigRcPtr & config, const char * displayName, const char * viewName) { + if (!displayName || !viewName) return; + const std::string name{ config->getDisplayViewColorSpaceName(displayName, viewName) }; const std::string csName{ name.empty() ? displayName : name }; if (csName.empty()) diff --git a/src/OpenColorIO/apphelpers/MixingHelpers.cpp b/src/OpenColorIO/apphelpers/MixingHelpers.cpp index 9f6fa1e396..1895c17a1b 100644 --- a/src/OpenColorIO/apphelpers/MixingHelpers.cpp +++ b/src/OpenColorIO/apphelpers/MixingHelpers.cpp @@ -205,6 +205,11 @@ void MixingColorSpaceManagerImpl::setSelectedMixingSpaceIdx(size_t idx) void MixingColorSpaceManagerImpl::setSelectedMixingSpace(const char * mixingSpace) { + if (!mixingSpace) + { + throw Exception("Invalid null mixing space name."); + } + for (size_t idx = 0 ; idx < m_mixingSpaces.size(); ++idx) { if (m_mixingSpaces[idx] == mixingSpace) @@ -269,6 +274,11 @@ void MixingColorSpaceManagerImpl::setSelectedMixingEncodingIdx(size_t idx) void MixingColorSpaceManagerImpl::setSelectedMixingEncoding(const char * mixingEncoding) { + if (!mixingEncoding) + { + throw Exception("Invalid null mixing encoding name."); + } + for (size_t idx = 0 ; idx < m_mixingEncodings.size(); ++idx) { if (m_mixingEncodings[idx] == mixingEncoding) diff --git a/src/OpenColorIO/apphelpers/mergeconfigs/MergeConfigsHelpers.cpp b/src/OpenColorIO/apphelpers/mergeconfigs/MergeConfigsHelpers.cpp index ab74ed9975..1342d533cd 100644 --- a/src/OpenColorIO/apphelpers/mergeconfigs/MergeConfigsHelpers.cpp +++ b/src/OpenColorIO/apphelpers/mergeconfigs/MergeConfigsHelpers.cpp @@ -477,16 +477,19 @@ ConstConfigRcPtr ConfigMerger::Impl::loadConfig(const char * value) const try { // Try to load the provided config using the search paths. - // Return as soon as they find a valid path. - const std::string resolvedfullpath = pystring::os::path::join(searchpaths[i], - value); + // Return as soon as a valid path is found. + // Normalize the path to prevent directory traversal via '../' sequences. + const std::string resolvedfullpath = pystring::os::path::normpath( + pystring::os::path::join(searchpaths[i], value)); return Config::CreateFromFile(resolvedfullpath.c_str()); } // TODO: If the file exists but won't load, this hides the error. // (Tried using ExceptionMissingFile, but the implementation of that is not what I // expected, Config::CreateFromFile only uses that if the argument is empty, not // if it can't read the file.) - catch(...) { /* don't capture the exception */ } + // There is no need to log a warning here, since this is simply trying the various + // locations on the path that might contain the config. + catch(...) { } } // Try to load the provided base config name as a built-in config. @@ -495,7 +498,9 @@ ConstConfigRcPtr ConfigMerger::Impl::loadConfig(const char * value) const // Check if the base config name is a built-in config. return Config::CreateFromBuiltinConfig(value); } - catch(...) { /* don't capture the exception */ } + // There is no need to log a warning here, since this is simply trying the various + // possible sources for the named config. + catch(...) { } // Must be a reference to a config from a previous merge. for (size_t i = 0; i < m_mergeParams.size(); i++) diff --git a/src/OpenColorIO/apphelpers/mergeconfigs/OCIOMYaml.cpp b/src/OpenColorIO/apphelpers/mergeconfigs/OCIOMYaml.cpp index 3afddf3474..f431c4ccdf 100644 --- a/src/OpenColorIO/apphelpers/mergeconfigs/OCIOMYaml.cpp +++ b/src/OpenColorIO/apphelpers/mergeconfigs/OCIOMYaml.cpp @@ -299,8 +299,7 @@ void OCIOMYaml::loadParams(const YAML::Node & node, ConfigMergingParametersRcPtr } else { - // Handle unsupported property or use default handler. - std::cout << "Unsupported property : " << key << std::endl; + LogWarning("Unsupported property in merge params: " + key); } } } @@ -324,15 +323,34 @@ void OCIOMYaml::load(const YAML::Node& node, ConfigMergerRcPtr & merger, const c load(node["ociom_version"], version); results = StringUtils::Split(version, '.'); - if(results.size() == 1) + bool faulty_version = false; + try { - merger->setVersion(std::stoi(results[0].c_str()), 0); + if(results.size() == 1) + { + merger->setVersion(std::stoi(results[0].c_str()), 0); + } + else if(results.size() == 2) + { + merger->setVersion(std::stoi(results[0].c_str()), + std::stoi(results[1].c_str())); + } + else + { + faulty_version = true; + } } - else if(results.size() == 2) + catch (const std::exception &) { - merger->setVersion(std::stoi(results[0].c_str()), - std::stoi(results[1].c_str())); + faulty_version = true; } + + if (faulty_version) + { + throwValueError(it->second.Tag(), it->first, + "The value '" + version + "' is not a valid OCIOM version."); + } + if (merger->getMajorVersion() > 1u || merger->getMinorVersion() > 0u) { throwValueError(it->second.Tag(), it->first, diff --git a/src/OpenColorIO/builtinconfigs/BuiltinConfigRegistry.cpp b/src/OpenColorIO/builtinconfigs/BuiltinConfigRegistry.cpp index bced3f4144..6fd3b7b52f 100644 --- a/src/OpenColorIO/builtinconfigs/BuiltinConfigRegistry.cpp +++ b/src/OpenColorIO/builtinconfigs/BuiltinConfigRegistry.cpp @@ -34,6 +34,8 @@ namespace OCIO_NAMESPACE // Note that this function does not require initializing the built-in config registry. const char * ResolveConfigPath(const char * originalPath) noexcept { + if (!originalPath) return ""; + static const std::regex uriPattern(R"(ocio:\/\/([^\s]+))"); std::smatch match; const std::string uri = originalPath; diff --git a/src/OpenColorIO/fileformats/FileFormat3DL.cpp b/src/OpenColorIO/fileformats/FileFormat3DL.cpp index c4d90cc776..59f0bdf2a4 100755 --- a/src/OpenColorIO/fileformats/FileFormat3DL.cpp +++ b/src/OpenColorIO/fileformats/FileFormat3DL.cpp @@ -314,6 +314,13 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, // If we've found 3 ints, add it to our 3D LUT. else if(tmpData.size() == 3) { + if (raw3d.size() > Max3DLUTLength * Max3DLUTLength * Max3DLUTLength * 3) + { + std::ostringstream os; + os << "Error parsing .3dl file. "; + os << "Too many 3D LUT entries found."; + throw Exception(os.str().c_str()); + } raw3d.push_back(tmpData[0]); raw3d.push_back(tmpData[1]); raw3d.push_back(tmpData[2]); diff --git a/src/OpenColorIO/fileformats/FileFormatCSP.cpp b/src/OpenColorIO/fileformats/FileFormatCSP.cpp index 418edfa570..aa48762ea5 100755 --- a/src/OpenColorIO/fileformats/FileFormatCSP.cpp +++ b/src/OpenColorIO/fileformats/FileFormatCSP.cpp @@ -487,9 +487,16 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, { // How many 1D LUT points do we have. nextline (istream, line); - int points1D = std::stoi(line.c_str()); + int points1D = 0; + if (!StringToInt(&points1D, line.c_str())) + { + std::ostringstream os; + os << "A csp 1D LUT with invalid number of entries ("; + os << line << ") in " << fileName << "."; + throw Exception(os.str().c_str()); + } - if (points1D <= 0) + if (points1D <= 0 || points1D > static_cast(Max1DLUTLength)) { std::ostringstream os; os << "A csp 1D LUT with invalid number of entries ("; @@ -563,7 +570,7 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, throw Exception(os.str().c_str()); } - if (lutSize <= 0) + if (lutSize <= 0 || lutSize > static_cast(Max3DLUTLength)) { std::ostringstream os; os << "A csp 3D LUT with invalid cube size ("; diff --git a/src/OpenColorIO/fileformats/FileFormatDiscreet1DL.cpp b/src/OpenColorIO/fileformats/FileFormatDiscreet1DL.cpp index 6a76d968d4..d22de1ba3c 100755 --- a/src/OpenColorIO/fileformats/FileFormatDiscreet1DL.cpp +++ b/src/OpenColorIO/fileformats/FileFormatDiscreet1DL.cpp @@ -275,15 +275,13 @@ bool Lut1dUtils::IMLutAlloc(IMLutStruct **plut, int num, int length) // targetBitDepth will be set appropriately for conversion LUTs in IMLutGet lut->targetBitDepth = IMLutTableSizeToBitDepth(length); - lut->tables = (unsigned short **)malloc(4 * sizeof(unsigned short *)); + lut->tables = (unsigned short **)calloc(4, sizeof(unsigned short *)); if (lut->tables == NULL) { delete lut; lut = 0; return false; } - for (i = 0; i < num; i++) - lut->tables[i] = NULL; for (i = 0; i < num; i++) { lut->tables[i] = (unsigned short *)malloc(length * sizeof(unsigned short)); @@ -324,7 +322,15 @@ static int tableLoad( if (isdigit(*InString)) { - ptable[Count++] = (unsigned short)std::stoi(InString); + try + { + ptable[Count++] = (unsigned short)std::stoi(InString); + } + catch (const std::exception &) + { + errorLine = InString; + return Lut1dUtils::IMLUT_ERR_SYNTAX; + } if (Count >= length) break; } @@ -404,22 +410,34 @@ int Lut1dUtils::IMLutGet( } // Load first table value. - (lut->tables[0])[0] = (unsigned short)std::stoi(InString); + try + { + (lut->tables[0])[0] = (unsigned short)std::stoi(InString); + } + catch (const std::exception &) + { + errorLine = InString; + status = IMLUT_ERR_SYNTAX; + IMLutFree(&lut); + *plut = 0; + goto load_abort; + } tablestart = 1; } else { char dstDepthS[16] = ""; #ifdef _WIN32 - const int nummatched = sscanf(InString, "%*s %d %d %s", &numtables, &length, dstDepthS, 16); + const int nummatched = sscanf_s(InString, "%*s %d %d %15s", &numtables, &length, dstDepthS, 16); #else - const int nummatched = sscanf(InString, "%*s %d %d %s", &numtables, &length, dstDepthS); + const int nummatched = sscanf(InString, "%*s %d %d %15s", &numtables, &length, dstDepthS); #endif std::string subStr(InString, 5); if (nummatched < 2 || StringUtils::Lower(subStr) != "lut: " || (numtables != 1 && numtables != 3 && numtables != 4) || - length <= 0) + length <= 0 || + length > 65536) { errorLine = InString; status = IMLUT_ERR_SYNTAX; @@ -435,7 +453,7 @@ int Lut1dUtils::IMLutGet( int dstDepth = 0; char floatC = ' '; #ifdef _WIN32 - sscanf(dstDepthS, "%d%c", &dstDepth, &floatC, 1); + sscanf_s(dstDepthS, "%d%c", &dstDepth, &floatC, 1); #else sscanf(dstDepthS, "%d%c", &dstDepth, &floatC); #endif diff --git a/src/OpenColorIO/fileformats/FileFormatHDL.cpp b/src/OpenColorIO/fileformats/FileFormatHDL.cpp index 16e0ffab53..f2ca6c0059 100755 --- a/src/OpenColorIO/fileformats/FileFormatHDL.cpp +++ b/src/OpenColorIO/fileformats/FileFormatHDL.cpp @@ -189,6 +189,13 @@ readLuts(std::istream& istream, if (result.ec == std::errc()) { + // Cap per-LUT entry count: max is Max3DLUTLength^3 * 3 for a 3D LUT. + if (lutValues[lutname].size() > Max3DLUTLength * Max3DLUTLength * Max3DLUTLength * 3) + { + std::ostringstream os; + os << "Too many values in " << lutname << " LUT block"; + throw Exception(os.str().c_str()); + } lutValues[lutname].push_back(v); } else @@ -452,6 +459,13 @@ LocalFileFormat::read(std::istream & istream, // Set cube size size_3d = lut_sizes[0]; + if(size_3d < 2 || size_3d > static_cast(Max3DLUTLength)) + { + std::ostringstream os; + os << "3D LUT cube size must be between 2 and " << Max3DLUTLength << ", found: " << size_3d; + throw Exception(os.str().c_str()); + } + lut3d_ptr = std::make_shared(lut_sizes[0]); if (Lut3DOpData::IsValidInterpolation(interp)) { @@ -463,11 +477,23 @@ LocalFileFormat::read(std::istream & istream, if(cachedFile->hdltype == "c") { size_1d = lut_sizes[0]; + if(size_1d < 2 || size_1d > static_cast(Max1DLUTLength)) + { + std::ostringstream os; + os << "1D LUT size must be between 2 and " << Max1DLUTLength << ", found: " << size_1d; + throw Exception(os.str().c_str()); + } } if(cachedFile->hdltype == "3d+1d") { size_prelut = lut_sizes[1]; + if(size_prelut < 2 || size_prelut > static_cast(Max1DLUTLength)) + { + std::ostringstream os; + os << "Prelut size must be between 2 and " << Max1DLUTLength << ", found: " << size_prelut; + throw Exception(os.str().c_str()); + } } } diff --git a/src/OpenColorIO/fileformats/FileFormatICC.cpp b/src/OpenColorIO/fileformats/FileFormatICC.cpp index 69c0145d8a..be105bf6d0 100755 --- a/src/OpenColorIO/fileformats/FileFormatICC.cpp +++ b/src/OpenColorIO/fileformats/FileFormatICC.cpp @@ -180,9 +180,14 @@ LocalCachedFileRcPtr LocalFileFormat::ReadInfo(std::istream & istream, ThrowErrorMessage("Error loading number of tags.", fileName); } + constexpr icUInt32Number MaxNumICCTags = 100; + if (count > MaxNumICCTags) + { + ThrowErrorMessage("Too many tags in ICC profile.", fileName); + } icc.mTags.resize(count); - // Read Tag offset table. + // Read Tag offset table. for (i = 0; i static_cast(Max1DLUTLength)) + { + ThrowErrorMessage( + ("'LUT_1D_SIZE' must be between 2 and " + + std::to_string(Max1DLUTLength) + ".").c_str(), + fileName, + lineNumber, + line); + } + raw.reserve(3*size1d); in1d = true; } @@ -231,6 +241,16 @@ LocalFileFormat::read(std::istream & istream, line); } + if (size3d < 2 || size3d > static_cast(Max3DLUTLength)) + { + ThrowErrorMessage( + ("'LUT_3D_SIZE' must be between 2 and " + + std::to_string(Max3DLUTLength) + ".").c_str(), + fileName, + lineNumber, + line); + } + raw.reserve(3*size3d*size3d*size3d); in3d = true; } @@ -241,9 +261,9 @@ LocalFileFormat::read(std::istream & istream, char domainMinB[64] = ""; #ifdef _WIN32 - if (sscanf_s(line.c_str(), "domain_min %s %s %s %c", domainMinR, 64, domainMinG, 64, domainMinB, 64, &endTok, 1) != 3) + if (sscanf_s(line.c_str(), "domain_min %63s %63s %63s %c", domainMinR, 64, domainMinG, 64, domainMinB, 64, &endTok, 1) != 3) #else - if (sscanf(line.c_str(), "domain_min %s %s %s %c", domainMinR, domainMinG, domainMinB, &endTok) != 3) + if (sscanf(line.c_str(), "domain_min %63s %63s %63s %c", domainMinR, domainMinG, domainMinB, &endTok) != 3) #endif { ThrowErrorMessage( @@ -275,9 +295,9 @@ LocalFileFormat::read(std::istream & istream, char domainMaxB[64] = ""; #ifdef _WIN32 - if (sscanf_s(line.c_str(), "domain_max %s %s %s %c", domainMaxR, 64, domainMaxG, 64, domainMaxB, 64, &endTok, 1) != 3) + if (sscanf_s(line.c_str(), "domain_max %63s %63s %63s %c", domainMaxR, 64, domainMaxG, 64, domainMaxB, 64, &endTok, 1) != 3) #else - if (sscanf(line.c_str(), "domain_max %s %s %s %c", domainMaxR, domainMaxG, domainMaxB, &endTok) != 3) + if (sscanf(line.c_str(), "domain_max %63s %63s %63s %c", domainMaxR, domainMaxG, domainMaxB, &endTok) != 3) #endif { ThrowErrorMessage( @@ -322,9 +342,9 @@ LocalFileFormat::read(std::istream & istream, char valB[64] = ""; #ifdef _WIN32 - if (sscanf_s(line.c_str(), "%s %s %s %c", valR, 64, valG, 64, valB, 64, &endTok, 1) != 3) + if (sscanf_s(line.c_str(), "%63s %63s %63s %c", valR, 64, valG, 64, valB, 64, &endTok, 1) != 3) #else - if (sscanf(line.c_str(), "%s %s %s %c", valR, valG, valB, &endTok) != 3) + if (sscanf(line.c_str(), "%63s %63s %63s %c", valR, valG, valB, &endTok) != 3) #endif { // It must be a float triple! diff --git a/src/OpenColorIO/fileformats/FileFormatIridasItx.cpp b/src/OpenColorIO/fileformats/FileFormatIridasItx.cpp index ae6282efb8..b7f84c59fa 100755 --- a/src/OpenColorIO/fileformats/FileFormatIridasItx.cpp +++ b/src/OpenColorIO/fileformats/FileFormatIridasItx.cpp @@ -165,6 +165,15 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, lineNumber, line); } + if (size < 2 || size > static_cast(Max3DLUTLength)) + { + ThrowErrorMessage( + ("LUT_3D_SIZE must be between 2 and " + + std::to_string(Max3DLUTLength) + ".").c_str(), + fileName, + lineNumber, + line); + } size3d = size; raw.reserve(3*size3d * size3d * size3d); @@ -184,6 +193,15 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, line); } + if (raw.size() > Max3DLUTLength * Max3DLUTLength * Max3DLUTLength * 3) + { + ThrowErrorMessage( + "Too many 3D LUT entries.", + fileName, + lineNumber, + line); + } + for(int i=0; i<3; ++i) { raw.push_back(tmpfloats[i]); diff --git a/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp b/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp index 0ba209a905..e2c30cbf5f 100755 --- a/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp +++ b/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp @@ -429,6 +429,13 @@ class XMLParserHelper os << "'. Expected quoted integer"; pImpl->Throw(os.str().c_str()); } + if (size_3d < 2 || size_3d > static_cast(Max3DLUTLength)) + { + std::ostringstream os; + os << "Invalid LUT size value: '" << size_raw; + os << "'. Expected value between 2 and " << Max3DLUTLength; + pImpl->Throw(os.str().c_str()); + } pImpl->m_lutSize = static_cast(size_3d); } else if (pImpl->m_data) @@ -441,7 +448,11 @@ class XMLParserHelper StringUtils::ReplaceInPlace(what, "\"", ""); StringUtils::ReplaceInPlace(what, "'", ""); StringUtils::ReplaceInPlace(what, "\n", ""); - // Append to lut string + // Append to lut string (cap at max possible: Max3DLUTLength^3 entries * 3 floats * 8 hex chars) + if (pImpl->m_lutString.size() + what.size() > Max3DLUTLength * Max3DLUTLength * Max3DLUTLength * 3 * 8) + { + pImpl->Throw("Too many characters in 'data' block"); + } pImpl->m_lutString += what; } } diff --git a/src/OpenColorIO/fileformats/FileFormatPandora.cpp b/src/OpenColorIO/fileformats/FileFormatPandora.cpp index 6d36321527..0d2baf5ea5 100755 --- a/src/OpenColorIO/fileformats/FileFormatPandora.cpp +++ b/src/OpenColorIO/fileformats/FileFormatPandora.cpp @@ -156,6 +156,16 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, lineNumber, line); } + if (inval < 8 || inval > static_cast(Max3DLUTLength * Max3DLUTLength * Max3DLUTLength)) + { + ThrowErrorMessage( + ("'in' value must be between 8 and " + + std::to_string(Max3DLUTLength * Max3DLUTLength * Max3DLUTLength) + + " (" + std::to_string(Max3DLUTLength) + "^3).").c_str(), + fileName, + lineNumber, + line); + } raw3d.reserve(inval*3); lutEdgeLen = Get3DLutEdgeLenFromNumPixels(inval); } @@ -213,6 +223,15 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, line); } + if (raw3d.size() > Max3DLUTLength * Max3DLUTLength * Max3DLUTLength * 3) + { + ThrowErrorMessage( + "Too many 3D LUT entries.", + fileName, + lineNumber, + line); + } + raw3d.push_back(tmpints[1]); raw3d.push_back(tmpints[2]); raw3d.push_back(tmpints[3]); diff --git a/src/OpenColorIO/fileformats/FileFormatResolveCube.cpp b/src/OpenColorIO/fileformats/FileFormatResolveCube.cpp index 6b5e0da8d7..cb43e2be71 100755 --- a/src/OpenColorIO/fileformats/FileFormatResolveCube.cpp +++ b/src/OpenColorIO/fileformats/FileFormatResolveCube.cpp @@ -336,6 +336,15 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, line); } + if (size1d < 2 || size1d > static_cast(Max1DLUTLength)) + { + ThrowErrorMessage( + ("LUT_1D_SIZE must be between 2 and " + + std::to_string(Max1DLUTLength) + ".").c_str(), + fileName, + lineNumber, + line); + } raw1d.reserve(3*size1d); has1d = true; } @@ -359,6 +368,15 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, line); } + if (size3d < 2 || size3d > static_cast(Max3DLUTLength)) + { + ThrowErrorMessage( + ("LUT_3D_SIZE must be between 2 and " + + std::to_string(Max3DLUTLength) + ".").c_str(), + fileName, + lineNumber, + line); + } raw3d.reserve(3*size3d*size3d*size3d); has3d = true; } @@ -410,6 +428,14 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, } else { + if (raw3d.size() > Max3DLUTLength * Max3DLUTLength * Max3DLUTLength * 3) + { + ThrowErrorMessage( + "Too many 3D LUT entries.", + fileName, + lineNumber, + line); + } raw3d.push_back(tmpfloats[i]); } } diff --git a/src/OpenColorIO/fileformats/FileFormatSpi1D.cpp b/src/OpenColorIO/fileformats/FileFormatSpi1D.cpp index 1cdc51b22c..c63e22ad25 100755 --- a/src/OpenColorIO/fileformats/FileFormatSpi1D.cpp +++ b/src/OpenColorIO/fileformats/FileFormatSpi1D.cpp @@ -135,9 +135,9 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, char fromMinS[64] = ""; char fromMaxS[64] = ""; #ifdef _WIN32 - if (sscanf_s(lineBuffer, "From %s %s", fromMinS, 64, fromMaxS, 64) != 2) + if (sscanf_s(lineBuffer, "From %63s %63s", fromMinS, 64, fromMaxS, 64) != 2) #else - if (sscanf(lineBuffer, "From %s %s", fromMinS, fromMaxS) != 2) + if (sscanf(lineBuffer, "From %63s %63s", fromMinS, fromMaxS) != 2) #endif { ThrowErrorMessage("Invalid 'From' Tag", currentLine, headerLine); @@ -179,6 +179,12 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, { ThrowErrorMessage("Could not find 'Length' Tag", -1, ""); } + if (lut_size < 2 || lut_size > static_cast(Max1DLUTLength)) + { + ThrowErrorMessage( + ("'Length' must be between 2 and " + std::to_string(Max1DLUTLength)).c_str(), + -1, ""); + } if (components == -1) { ThrowErrorMessage("Could not find 'Components' Tag", -1, ""); @@ -219,11 +225,11 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, char inputLUT[4][64] = {"", "", "", ""}; #ifdef _WIN32 - if (sscanf_s(lineBuffer, "%s %s %s %63s", inputLUT[0], 64, + if (sscanf_s(lineBuffer, "%63s %63s %63s %63s", inputLUT[0], 64, inputLUT[1], 64, inputLUT[2], 64, inputLUT[3], 64) != components) #else - if (sscanf(lineBuffer, "%s %s %s %63s", inputLUT[0], + if (sscanf(lineBuffer, "%63s %63s %63s %63s", inputLUT[0], inputLUT[1], inputLUT[2], inputLUT[3]) != components) #endif { diff --git a/src/OpenColorIO/fileformats/FileFormatSpi3D.cpp b/src/OpenColorIO/fileformats/FileFormatSpi3D.cpp index c3ef153f43..af8150254e 100755 --- a/src/OpenColorIO/fileformats/FileFormatSpi3D.cpp +++ b/src/OpenColorIO/fileformats/FileFormatSpi3D.cpp @@ -128,6 +128,17 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, throw Exception(os.str().c_str()); } + if (rSize < 2 || rSize > static_cast(Max3DLUTLength)) + { + std::ostringstream os; + os << "Error parsing .spi3d file ("; + os << fileName; + os << "). "; + os << "LUT size must be between 2 and " << Max3DLUTLength << ". Found: '"; + os << lineBuffer << "'."; + throw Exception(os.str().c_str()); + } + Lut3DOpDataRcPtr lut3d = std::make_shared((unsigned long)rSize); if (Lut3DOpData::IsValidInterpolation(interp)) { @@ -153,14 +164,13 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, char blueValueS[64] = ""; #ifdef _WIN32 - if (sscanf(lineBuffer, - "%d %d %d %s %s %s", + if (sscanf_s(lineBuffer, "%d %d %d %63s %63s %63s", &rIndex, &gIndex, &bIndex, redValueS, 64, greenValueS, 64, blueValueS, 64) == 6) #else - if (sscanf(lineBuffer, "%d %d %d %s %s %s", + if (sscanf(lineBuffer, "%d %d %d %63s %63s %63s", &rIndex, &gIndex, &bIndex, redValueS, greenValueS, blueValueS) == 6) #endif diff --git a/src/OpenColorIO/fileformats/FileFormatSpiMtx.cpp b/src/OpenColorIO/fileformats/FileFormatSpiMtx.cpp index 663a4e9a5d..87c15182dd 100755 --- a/src/OpenColorIO/fileformats/FileFormatSpiMtx.cpp +++ b/src/OpenColorIO/fileformats/FileFormatSpiMtx.cpp @@ -68,23 +68,23 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, Interpolation /*interp*/) const { - // Read the entire file. - std::ostringstream fileStream; - + // Read the entire file (capped: a valid spimtx file contains exactly 12 floats). + constexpr size_t MAX_FILE_SIZE = 1024; + char fileBuf[MAX_FILE_SIZE]; + istream.read(fileBuf, MAX_FILE_SIZE); + const std::streamsize bytesRead = istream.gcount(); + if (!istream.eof()) { - const int MAX_LINE_SIZE = 4096; - char lineBuffer[MAX_LINE_SIZE]; - - while (istream.good()) - { - istream.getline(lineBuffer, MAX_LINE_SIZE); - fileStream << std::string(lineBuffer) << " "; - } + std::ostringstream os; + os << "Error parsing .spimtx file ("; + os << fileName << "). "; + os << "File is too large to be a valid .spimtx file."; + throw Exception(os.str().c_str()); } // Turn it into parts - const StringUtils::StringVec lineParts - = StringUtils::SplitByWhiteSpaces(StringUtils::Trim(fileStream.str())); + const StringUtils::StringVec lineParts + = StringUtils::SplitByWhiteSpaces(StringUtils::Trim(std::string(fileBuf, bytesRead))); if(lineParts.size() != 12) { diff --git a/src/OpenColorIO/fileformats/FileFormatTruelight.cpp b/src/OpenColorIO/fileformats/FileFormatTruelight.cpp index 0d95261cc3..1c5b9452e2 100755 --- a/src/OpenColorIO/fileformats/FileFormatTruelight.cpp +++ b/src/OpenColorIO/fileformats/FileFormatTruelight.cpp @@ -149,6 +149,12 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, throw Exception(os.str().c_str()); } + if (size3d[0] < 2 || size3d[0] > static_cast(Max3DLUTLength)) + { + throw Exception(("Truelight .cub LUT grid size must be between 2 and " + + std::to_string(Max3DLUTLength) + ".").c_str()); + } + raw3d.reserve(3*size3d[0]*size3d[1]*size3d[2]); } else if(parts[1] == "lutlength") @@ -158,6 +164,12 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, { throw Exception("Malformed lutlength tag in Truelight .cub LUT."); } + if (size1d < 2 || size1d > static_cast(Max1DLUTLength)) + { + throw Exception(("Truelight .cub LUT lutlength must be between 2 and " + + std::to_string(Max1DLUTLength) + ".").c_str()); + } + raw1d.reserve(3*size1d); } else if(parts[1] == "inputlut") @@ -187,12 +199,20 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, { if(in1d) { + if (raw1d.size() > Max1DLUTLength * 3) + { + throw Exception("Too many 1D LUT entries in Truelight .cub LUT."); + } raw1d.push_back(tmpfloats[0]); raw1d.push_back(tmpfloats[1]); raw1d.push_back(tmpfloats[2]); } else if(in3d) { + if (raw3d.size() > Max3DLUTLength * Max3DLUTLength * Max3DLUTLength * 3) + { + throw Exception("Too many 3D LUT entries in Truelight .cub LUT."); + } raw3d.push_back(tmpfloats[0]); raw3d.push_back(tmpfloats[1]); raw3d.push_back(tmpfloats[2]); diff --git a/src/OpenColorIO/fileformats/FileFormatVF.cpp b/src/OpenColorIO/fileformats/FileFormatVF.cpp index 9026030344..736bf2cc19 100755 --- a/src/OpenColorIO/fileformats/FileFormatVF.cpp +++ b/src/OpenColorIO/fileformats/FileFormatVF.cpp @@ -158,6 +158,14 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, fileName, lineNumber, line); } + if (size3d[0] < 2 || size3d[0] > static_cast(Max3DLUTLength)) + { + ThrowErrorMessage( + ("Grid size must be between 2 and " + + std::to_string(Max3DLUTLength) + ".").c_str(), + fileName, lineNumber, line); + } + raw3d.reserve(3*size3d[0]*size3d[1]*size3d[2]); } else if(parts[0] == "global_transform") @@ -190,6 +198,12 @@ CachedFileRcPtr LocalFileFormat::read(std::istream & istream, { if(StringVecToFloatVec(tmpfloats, parts) && (tmpfloats.size() == 3)) { + if (raw3d.size() > Max3DLUTLength * Max3DLUTLength * Max3DLUTLength * 3) + { + ThrowErrorMessage( + "Too many 3D LUT entries.", + fileName, lineNumber, line); + } raw3d.push_back(tmpfloats[0]); raw3d.push_back(tmpfloats[1]); raw3d.push_back(tmpfloats[2]); diff --git a/src/OpenColorIO/fileformats/cdl/CDLParser.cpp b/src/OpenColorIO/fileformats/cdl/CDLParser.cpp index 1c02d7670e..e2945bdfbb 100644 --- a/src/OpenColorIO/fileformats/cdl/CDLParser.cpp +++ b/src/OpenColorIO/fileformats/cdl/CDLParser.cpp @@ -368,7 +368,6 @@ void CDLParser::Impl::reset() m_elms.clear(); m_lineNumber = 0; - m_fileName = ""; m_isCC = false; m_isCCC = false; } @@ -897,6 +896,11 @@ void CDLParser::Impl::CharacterDataHandler(void *userData, // Parsing a single new line. This is valid. if (len == 1 && s[0] == '\n') return; + if (pImpl->m_elms.empty()) + { + pImpl->throwMessage("Unexpected character data before root element"); + } + ElementRcPtr pElt = pImpl->m_elms.back(); if (!pElt) { diff --git a/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp b/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp index 1bf162db35..60e72b42c8 100644 --- a/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp +++ b/src/OpenColorIO/fileformats/ctf/CTFReaderHelper.cpp @@ -3458,6 +3458,11 @@ ArrayBase * CTFReaderInvLut1DElt::updateDimension(const Dimensions & dims) return nullptr; } + if (dims[0] < 2 || dims[0] > Max1DLUTLength) + { + return nullptr; + } + Array * pArray = &m_invLut->getArray(); pArray->resize(dims[0], numColorComponents); return pArray; @@ -3596,6 +3601,11 @@ ArrayBase * CTFReaderInvLut3DElt::updateDimension(const Dimensions & dims) return nullptr; } + if (dims[0] < 2 || dims[0] > Max3DLUTLength) + { + return nullptr; + } + Array * pArray = &m_invLut->getArray(); pArray->resize(dims[0], numColorComponents); return pArray; @@ -4271,6 +4281,11 @@ ArrayBase * CTFReaderLut1DElt::updateDimension(const Dimensions & dims) return nullptr; } + if (dims[0] < 2 || dims[0] > Max1DLUTLength) + { + return nullptr; + } + Array * pArray = &m_lut->getArray(); pArray->resize(dims[0], numColorComponents); return pArray; @@ -4332,7 +4347,7 @@ IndexMapping * CTFReaderLut1DElt::updateDimensionIM(const DimensionsIM & dims) const unsigned int numComponents = dims[0]; - if (dims[0] == 0) + if (dims[0] < 2 || dims[0] > Max1DLUTLength) { return nullptr; } @@ -4543,6 +4558,11 @@ ArrayBase * CTFReaderLut3DElt::updateDimension(const Dimensions & dims) return nullptr; } + if (dims[0] < 2 || dims[0] > Max3DLUTLength) + { + return nullptr; + } + Array* pArray = &m_lut->getArray(); pArray->resize(dims[0], numColorComponents); @@ -4573,7 +4593,7 @@ IndexMapping * CTFReaderLut3DElt::updateDimensionIM(const DimensionsIM & dims) const unsigned numComponents = dims[0]; - if (dims[0] == 0) + if (dims[0] < 2 || dims[0] > Max3DLUTLength) { return nullptr; } @@ -4677,6 +4697,11 @@ ArrayBase * CTFReaderMatrixElt::updateDimension(const Dimensions & dims) return nullptr; } + if (size < 3 || size > 4) + { + return nullptr; + } + ArrayDouble * pArray = &m_matrix->getArray(); pArray->resize(size, numColorComponents); diff --git a/src/OpenColorIO/ops/exponent/ExponentOp.cpp b/src/OpenColorIO/ops/exponent/ExponentOp.cpp index 41b95992c4..e10eb56b71 100644 --- a/src/OpenColorIO/ops/exponent/ExponentOp.cpp +++ b/src/OpenColorIO/ops/exponent/ExponentOp.cpp @@ -41,6 +41,10 @@ ExponentOpData::ExponentOpData(const ExponentOpData & rhs) ExponentOpData::ExponentOpData(const double * exp4) : OpData() { + if (!exp4) + { + throw Exception("ExponentOpData: exp4 must not be null."); + } memcpy(m_exp4, exp4, 4*sizeof(double)); } diff --git a/src/OpenColorIO/ops/gradingprimary/GradingPrimaryOpGPU.cpp b/src/OpenColorIO/ops/gradingprimary/GradingPrimaryOpGPU.cpp index a0d2298f0c..8ada40bdcf 100644 --- a/src/OpenColorIO/ops/gradingprimary/GradingPrimaryOpGPU.cpp +++ b/src/OpenColorIO/ops/gradingprimary/GradingPrimaryOpGPU.cpp @@ -501,10 +501,11 @@ void AddGPVideoInverseShader(GpuShaderCreatorRcPtr & shaderCreator, st.newLine() << pxl << ".rgb = ( " << pxl << ".rgb - " << props.pivotBlack << " ) * "<< props.slope <<" + " << props.pivotBlack << ";"; - st.newLine() << pxl << ".rgb += " << props.offset << " );"; -} + st.newLine() << pxl << ".rgb += " << props.offset << ";"; } +} // namespace + void GetGradingPrimaryGPUShaderProgram(GpuShaderCreatorRcPtr & shaderCreator, ConstGradingPrimaryOpDataRcPtr & gpData) { diff --git a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp index 8dc8d8d23d..d046111651 100644 --- a/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp +++ b/src/OpenColorIO/ops/gradingrgbcurve/GradingBSplineCurve.cpp @@ -1155,7 +1155,12 @@ void GradingBSplineCurveImpl::AddShaderEvalRev(GpuShaderText & st, st.newLine() << "float kn = " << knots << "[knotsOffs + i];"; st.newLine() << "float C0 = C - x;"; st.newLine() << "float discrim = sqrt(B * B - 4. * A * C0);"; - st.newLine() << "return kn + (-2. * C0) / (discrim + B);"; + st.newLine() << "float denom = discrim + B;"; + st.newLine() << "if (abs(denom) < 1e-5)"; + st.newLine() << "{"; + st.newLine() << " return abs(B) < 1e-5 ? kn : kn + (-C0 / B);"; + st.newLine() << "}"; + st.newLine() << "return kn + (-2. * C0) / denom;"; } //------------------------------------------------------------------------------------------------ @@ -1229,7 +1234,12 @@ void GradingBSplineCurveImpl::AddShaderEvalRevHue(GpuShaderText & st, st.newLine() << "}"; st.newLine() << "float C0 = C - x;"; st.newLine() << "float discrim = sqrt(B * B - 4. * A * C0);"; - st.newLine() << "return kn + (-2. * C0) / (discrim + B);"; + st.newLine() << "float denom = discrim + B;"; + st.newLine() << "if (abs(denom) < 1e-5)"; + st.newLine() << "{"; + st.newLine() << " return abs(B) < 1e-5 ? kn : kn + (-C0 / B);"; + st.newLine() << "}"; + st.newLine() << "return kn + (-2. * C0) / denom;"; } //------------------------------------------------------------------------------------------------ @@ -1360,7 +1370,13 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRev(int c, float y) const const float kn = m_knotsArray[knotsOffs + i]; const float C0 = C - y; const float discrim = sqrt(B * B - 4.f * A * C0); - return kn + (-2.f * C0) / (discrim + B); + const float denom = discrim + B; + if (std::fabs(denom) < 1e-5f) + { + // A~=0, B<0: linear segment with negative slope; use linear inverse. + return std::fabs(B) < 1e-5f ? kn : kn + (-C0 / B); + } + return kn + (-2.f * C0) / denom; } } @@ -1432,7 +1448,13 @@ float GradingBSplineCurveImpl::KnotsCoefs::evalCurveRevHue(int c, float y) const } const float C0 = C - y; const float discrim = sqrt(B * B - 4.f * A * C0); - return kn + (-2.f * C0) / (discrim + B); + const float denom = discrim + B; + if (std::fabs(denom) < 1e-5f) + { + // A~=0, B<0: linear segment with negative slope; use linear inverse. + return std::fabs(B) < 1e-5f ? kn : kn + (-C0 / B); + } + return kn + (-2.f * C0) / denom; // Note: The caller should wrap to ensure the output is a hue on [0,1). } diff --git a/src/OpenColorIO/ops/lut3d/Lut3DOpData.cpp b/src/OpenColorIO/ops/lut3d/Lut3DOpData.cpp index e17cbcfce6..2331d1777b 100644 --- a/src/OpenColorIO/ops/lut3d/Lut3DOpData.cpp +++ b/src/OpenColorIO/ops/lut3d/Lut3DOpData.cpp @@ -58,9 +58,6 @@ Lut3DOpDataRcPtr MakeFastLut3DFromInverse(ConstLut3DOpDataRcPtr & lut) return result; } -// 129 allows for a MESH dimension of 7 in the 3dl file format. -const unsigned long Lut3DOpData::maxSupportedLength = 129; - // Functional composition is a concept from mathematics where two functions // are combined into a single function. This idea may be applied to ops // where we generate a single op that has the same (or similar) effect as @@ -196,11 +193,11 @@ void Lut3DOpData::Lut3DArray::fill() void Lut3DOpData::Lut3DArray::resize(unsigned long length, unsigned long numColorComponents) { - if (length > maxSupportedLength) + if (length > Max3DLUTLength) { std::ostringstream oss; oss << "LUT 3D: Grid size '" << length - << "' must not be greater than '" << maxSupportedLength << "'."; + << "' must not be greater than '" << Max3DLUTLength << "'."; throw Exception(oss.str().c_str()); } Array::resize(length, numColorComponents); @@ -389,7 +386,7 @@ void Lut3DOpData::validate() const throw Exception("Lut3D has an incorrect number of color components. "); } - if (getArray().getLength()>maxSupportedLength) + if (getArray().getLength()>Max3DLUTLength) { // This should never happen. Enforced by resize. std::ostringstream oss; diff --git a/src/OpenColorIO/ops/lut3d/Lut3DOpData.h b/src/OpenColorIO/ops/lut3d/Lut3DOpData.h index 370f496c74..c10f2af3eb 100644 --- a/src/OpenColorIO/ops/lut3d/Lut3DOpData.h +++ b/src/OpenColorIO/ops/lut3d/Lut3DOpData.h @@ -8,6 +8,7 @@ #include +#include "LutLimits.h" #include "Op.h" #include "ops/OpArray.h" #include "PrivateTypes.h" @@ -23,9 +24,6 @@ class Lut3DOpData : public OpData { public: - // The maximum grid size supported for a 3D LUT. - static const unsigned long maxSupportedLength; - // Use functional composition to generate a single op that // approximates the effect of the pair of ops. static Lut3DOpDataRcPtr Compose(ConstLut3DOpDataRcPtr & lut1, ConstLut3DOpDataRcPtr & lut2); diff --git a/src/OpenColorIO/ops/matrix/MatrixOp.cpp b/src/OpenColorIO/ops/matrix/MatrixOp.cpp index 93505a284e..b593983128 100644 --- a/src/OpenColorIO/ops/matrix/MatrixOp.cpp +++ b/src/OpenColorIO/ops/matrix/MatrixOp.cpp @@ -304,7 +304,12 @@ void CreateMinMaxOp(OpRcPtrVec & ops, bool somethingToDo = false; for (int i = 0; i < 3; ++i) { - scale4[i] = 1.0 / (from_max3[i] - from_min3[i]); + const double range = from_max3[i] - from_min3[i]; + if (range == 0.0) + { + throw Exception("CreateMinMaxOp: from_min and from_max must not be equal."); + } + scale4[i] = 1.0 / range; offset4[i] = -from_min3[i] * scale4[i]; somethingToDo |= (scale4[i] != 1.0 || offset4[i] != 0.0); } diff --git a/src/OpenColorIO/ops/matrix/MatrixOpData.cpp b/src/OpenColorIO/ops/matrix/MatrixOpData.cpp index 9750b154eb..709cde55ca 100644 --- a/src/OpenColorIO/ops/matrix/MatrixOpData.cpp +++ b/src/OpenColorIO/ops/matrix/MatrixOpData.cpp @@ -373,6 +373,10 @@ void MatrixOpData::MatrixArray::expandFrom3x3To4x4() void MatrixOpData::MatrixArray::setRGBA(const float * values) { + if (!values) + { + throw Exception("Matrix: setRGBA NULL pointer."); + } Values & v = getValues(); v[0] = values[0]; @@ -398,6 +402,10 @@ void MatrixOpData::MatrixArray::setRGBA(const float * values) void MatrixOpData::MatrixArray::setRGBA(const double * values) { + if (!values) + { + throw Exception("Matrix: setRGBA NULL pointer."); + } Values & v = getValues(); std::memcpy(&v[0], values, 16 * sizeof(double)); } diff --git a/src/OpenColorIO/transforms/AllocationTransform.cpp b/src/OpenColorIO/transforms/AllocationTransform.cpp index eeca74431d..1ab8aea5b6 100755 --- a/src/OpenColorIO/transforms/AllocationTransform.cpp +++ b/src/OpenColorIO/transforms/AllocationTransform.cpp @@ -146,6 +146,10 @@ void AllocationTransform::setVars(int numvars, const float * vars) if(!getImpl()->m_vars.empty()) { + if (!vars) + { + throw Exception("AllocationTransform::setVars: vars pointer must not be null."); + } memcpy(&getImpl()->m_vars[0], vars, numvars*sizeof(float)); diff --git a/src/OpenColorIO/transforms/FileTransform.cpp b/src/OpenColorIO/transforms/FileTransform.cpp index 33da5f3077..6fd1549d61 100755 --- a/src/OpenColorIO/transforms/FileTransform.cpp +++ b/src/OpenColorIO/transforms/FileTransform.cpp @@ -110,7 +110,7 @@ const char * FileTransform::getSrc() const void FileTransform::setSrc(const char * src) { - getImpl()->m_src = src; + getImpl()->m_src = src ? src : ""; } const char * FileTransform::getCCCId() const @@ -120,7 +120,7 @@ const char * FileTransform::getCCCId() const void FileTransform::setCCCId(const char * cccid) { - getImpl()->m_cccid = cccid; + getImpl()->m_cccid = cccid ? cccid : ""; } CDLStyle FileTransform::getCDLStyle() const diff --git a/src/OpenColorIO/transforms/FixedFunctionTransform.cpp b/src/OpenColorIO/transforms/FixedFunctionTransform.cpp index 07baef12a9..1c00c8c0e8 100644 --- a/src/OpenColorIO/transforms/FixedFunctionTransform.cpp +++ b/src/OpenColorIO/transforms/FixedFunctionTransform.cpp @@ -21,8 +21,15 @@ FixedFunctionTransformRcPtr FixedFunctionTransform::Create(FixedFunctionStyle st const double * params, size_t num) { + if (!params && num > 0) + { + throw Exception("FixedFunctionTransform::Create: params pointer must not be null."); + } FixedFunctionOpData::Params p(num); - std::copy(params, params + num, p.begin()); + if (num > 0) + { + std::copy(params, params + num, p.begin()); + } return FixedFunctionTransformRcPtr(new FixedFunctionTransformImpl(style, p), &FixedFunctionTransformImpl::deleter); @@ -125,8 +132,15 @@ size_t FixedFunctionTransformImpl::getNumParams() const void FixedFunctionTransformImpl::setParams(const double * params, size_t num) { + if (!params && num > 0) + { + throw Exception("FixedFunctionTransform::setParams: params pointer must not be null."); + } FixedFunctionOpData::Params p(num); - std::copy(params, params+num, p.begin()); + if (num > 0) + { + std::copy(params, params + num, p.begin()); + } data().setParams(p); } diff --git a/src/OpenColorIO/transforms/Lut3DTransform.cpp b/src/OpenColorIO/transforms/Lut3DTransform.cpp index 6e1a2babeb..d0d12d4087 100644 --- a/src/OpenColorIO/transforms/Lut3DTransform.cpp +++ b/src/OpenColorIO/transforms/Lut3DTransform.cpp @@ -201,9 +201,9 @@ std::ostream & operator<< (std::ostream & os, const Lut3DTransform & t) rMin = std::min(rMin, rv); gMin = std::min(gMin, gv); bMin = std::min(bMin, bv); - rMax = std::max(rMin, rv); - gMax = std::max(gMin, gv); - bMax = std::max(bMin, bv); + rMax = std::max(rMax, rv); + gMax = std::max(gMax, gv); + bMax = std::max(bMax, bv); } } } diff --git a/src/OpenColorIO/transforms/builtins/BuiltinTransformRegistry.cpp b/src/OpenColorIO/transforms/builtins/BuiltinTransformRegistry.cpp index d828f11ace..f06515bcc7 100644 --- a/src/OpenColorIO/transforms/builtins/BuiltinTransformRegistry.cpp +++ b/src/OpenColorIO/transforms/builtins/BuiltinTransformRegistry.cpp @@ -124,7 +124,7 @@ void BuiltinTransformRegistryImpl::registerAll() noexcept void CreateBuiltinTransformOps(OpRcPtrVec & ops, size_t nameIndex, TransformDirection direction) { - if (nameIndex > BuiltinTransformRegistry::Get()->getNumBuiltins()) + if (nameIndex >= BuiltinTransformRegistry::Get()->getNumBuiltins()) { throw Exception("Invalid built-in transform name."); } diff --git a/src/utils/StringUtils.h b/src/utils/StringUtils.h index 7dbfaa1a4b..25600a2760 100644 --- a/src/utils/StringUtils.h +++ b/src/utils/StringUtils.h @@ -252,6 +252,8 @@ inline std::string::size_type ReverseFind(const std::string & subject, const std // In place replace the 'search' substring by the 'replace' string in 'str'. inline bool ReplaceInPlace(std::string & subject, const std::string & search, const std::string & replace) { + if (search.empty()) return false; + bool changed = false; size_t pos = 0; diff --git a/tests/cpu/ops/lut3d/Lut3DOpData_tests.cpp b/tests/cpu/ops/lut3d/Lut3DOpData_tests.cpp index 69d2234b92..27d7f6d537 100644 --- a/tests/cpu/ops/lut3d/Lut3DOpData_tests.cpp +++ b/tests/cpu/ops/lut3d/Lut3DOpData_tests.cpp @@ -67,8 +67,8 @@ OCIO_ADD_TEST(Lut3DOpData, clone) OCIO_ADD_TEST(Lut3DOpData, not_supported_length) { - OCIO_CHECK_NO_THROW(OCIO::Lut3DOpData{ OCIO::Lut3DOpData::maxSupportedLength }); - OCIO_CHECK_THROW_WHAT(OCIO::Lut3DOpData{ OCIO::Lut3DOpData::maxSupportedLength + 1 }, + OCIO_CHECK_NO_THROW(OCIO::Lut3DOpData{ OCIO::Max3DLUTLength }); + OCIO_CHECK_THROW_WHAT(OCIO::Lut3DOpData{ OCIO::Max3DLUTLength + 1 }, OCIO::Exception, "must not be greater"); } diff --git a/tests/gpu/GradingPrimaryOp_test.cpp b/tests/gpu/GradingPrimaryOp_test.cpp index 86dd540a4f..8ade7ae568 100644 --- a/tests/gpu/GradingPrimaryOp_test.cpp +++ b/tests/gpu/GradingPrimaryOp_test.cpp @@ -133,7 +133,7 @@ OCIO_ADD_GPU_TEST(GradingPrimary, style_lin_rev_dynamic) namespace GPTest3 { -static constexpr OCIO::GradingStyle style = OCIO::GRADING_LIN; +static constexpr OCIO::GradingStyle style = OCIO::GRADING_VIDEO; static const OCIO::GradingRGBM lift{ 0.05, -0.04, 0.02, 0.05 }; static const OCIO::GradingRGBM gamma{ 0.9, 1.4, 0.7, 0.75 }; static const OCIO::GradingRGBM gain{ 1.2, 1.1, 1.25, 0.8 }; diff --git a/tests/gpu/Lut3DOp_test.cpp b/tests/gpu/Lut3DOp_test.cpp index d1ee3aa3de..3224b64bb1 100644 --- a/tests/gpu/Lut3DOp_test.cpp +++ b/tests/gpu/Lut3DOp_test.cpp @@ -8,6 +8,7 @@ #include #include "GPUUnitTest.h" +#include "LutLimits.h" namespace OCIO = OCIO_NAMESPACE; @@ -241,7 +242,7 @@ OCIO_ADD_GPU_TEST(Lut3DOp, 3dlut_biggest_supported) { // Linear interpolation OCIO::Lut3DTransformRcPtr lut = OCIO::Lut3DTransform::Create(); - lut->setGridSize(129); // Lut3DOpData::maxSupportedLength. + lut->setGridSize(OCIO::Max3DLUTLength); test.setProcessor(lut);