From 69cafe64b4c965a96c7a53d48d5870f90d6c9ffc Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Mon, 8 Nov 2021 14:41:13 +0000 Subject: [PATCH 1/3] Cap glTFv1 & 2 json size to ~4GB Ensures size_t cannot overflow Limits the maximum contiguous memory allocation to something plausible. --- code/AssetLib/glTF/glTFAsset.inl | 13 +++++++++++-- code/AssetLib/glTF2/glTF2Asset.inl | 17 +++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/glTF/glTFAsset.inl b/code/AssetLib/glTF/glTFAsset.inl index 387fd27df..353dd5aab 100644 --- a/code/AssetLib/glTF/glTFAsset.inl +++ b/code/AssetLib/glTF/glTFAsset.inl @@ -1148,7 +1148,7 @@ inline void Asset::ReadBinaryHeader(IOStream &stream) { AI_SWAP4(header.length); AI_SWAP4(header.sceneLength); - mSceneLength = static_cast(header.sceneLength); + mSceneLength = static_cast(header.sceneLength); // Can't be larger than 4GB (max. uint32_t) mBodyOffset = sizeof(header) + mSceneLength; mBodyOffset = (mBodyOffset + 3) & ~3; // Round up to next multiple of 4 @@ -1179,8 +1179,17 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) { mBodyLength = 0; } - // read the scene data + // Smallest legal JSON file is "{}" Smallest loadable glTF file is larger than that but catch it later + if (mSceneLength < 2) { + throw DeadlyImportError("GLTF: No JSON file contents"); + } + // Binary format only supports up to 4GB of JSON so limit it there to avoid extreme memory allocation + if (mSceneLength > std::numeric_limits::max()) { + throw DeadlyImportError("GLTF: JSON size greater than 4GB"); + } + + // read the scene data, ensure null termination std::vector sceneData(mSceneLength + 1); sceneData[mSceneLength] = '\0'; diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index d65b4132b..d9d783e47 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1777,9 +1777,9 @@ inline void Asset::ReadBinaryHeader(IOStream &stream, std::vector &sceneDa throw DeadlyImportError("GLTF: JSON chunk missing"); } - // read the scene data + // read the scene data, ensure null termination - mSceneLength = chunk.chunkLength; + mSceneLength = chunk.chunkLength; // Can't be larger than 4GB (max. uint32_t) sceneData.resize(mSceneLength + 1); sceneData[mSceneLength] = '\0'; @@ -1835,9 +1835,13 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) { } else { mSceneLength = stream->FileSize(); mBodyLength = 0; + + // Binary format only supports up to 4GB of JSON, use that as a maximum + if (mSceneLength > std::numeric_limits::max()) { + throw DeadlyImportError("GLTF: JSON size greater than 4GB"); + } - // read the scene data - + // read the scene data, ensure null termination sceneData.resize(mSceneLength + 1); sceneData[mSceneLength] = '\0'; @@ -1846,6 +1850,11 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) { } } + // Smallest legal JSON file is "{}" Smallest loadable glTF file is larger than that but catch it later + if (mSceneLength < 2) { + throw DeadlyImportError("GLTF: No JSON file contents"); + } + // parse the JSON document ASSIMP_LOG_DEBUG("Parsing GLTF2 JSON"); Document doc; From 9433fc526a775bb290d477f92add5189fc3682eb Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Mon, 8 Nov 2021 14:42:21 +0000 Subject: [PATCH 2/3] Apply clangformat --- code/AssetLib/glTF/glTFAsset.inl | 103 ++++++++++++++-------------- code/AssetLib/glTF2/glTF2Asset.inl | 105 ++++++++++++++--------------- 2 files changed, 103 insertions(+), 105 deletions(-) diff --git a/code/AssetLib/glTF/glTFAsset.inl b/code/AssetLib/glTF/glTFAsset.inl index 353dd5aab..896a9f16c 100644 --- a/code/AssetLib/glTF/glTFAsset.inl +++ b/code/AssetLib/glTF/glTFAsset.inl @@ -39,8 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -#include #include +#include #include // Header files, Assimp @@ -57,11 +57,10 @@ using namespace glTFCommon; namespace glTF { #if _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4706) +#pragma warning(push) +#pragma warning(disable : 4706) #endif // _MSC_VER - // // LazyDict methods // @@ -214,9 +213,10 @@ inline void Buffer::Read(Value &obj, Asset &r) { } else { // Local file if (byteLength > 0) { std::string dir = !r.mCurrentAssetDir.empty() ? ( - r.mCurrentAssetDir.back() == '/' ? - r.mCurrentAssetDir : r.mCurrentAssetDir + '/' - ) : ""; + r.mCurrentAssetDir.back() == '/' ? + r.mCurrentAssetDir : + r.mCurrentAssetDir + '/') : + ""; IOStream *file = r.OpenFile(dir + uri, "rb"); if (file) { @@ -734,8 +734,8 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { ASSIMP_LOG_INFO("GLTF: Decompressing Open3DGC data."); /************** Read data from JSON-document **************/ -#define MESH_READ_COMPRESSEDDATA_MEMBER(pFieldName, pOut) \ - if (!ReadMember(*comp_data, pFieldName, pOut)) { \ +#define MESH_READ_COMPRESSEDDATA_MEMBER(pFieldName, pOut) \ + if (!ReadMember(*comp_data, pFieldName, pOut)) { \ throw DeadlyImportError("GLTF: \"compressedData\" must has \"", pFieldName, "\"."); \ } @@ -771,8 +771,7 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { Decode_O3DGC(*ext_o3dgc, pAsset_Root); Extension.push_back(ext_o3dgc); // store info in mesh extensions list. } // if(it_memb->name.GetString() == "Open3DGC-compression") - else - { + else { throw DeadlyImportError("GLTF: Unknown mesh extension: \"", it_memb->name.GetString(), "\"."); } } // for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); json_extensions++) @@ -842,21 +841,21 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG size_t tval = ifs.GetNFloatAttribute(static_cast(idx)); switch (ifs.GetFloatAttributeType(static_cast(idx))) { - case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD: - // Check situation when encoded data contain texture coordinates but primitive not. - if (idx_texcoord < primitives[0].attributes.texcoord.size()) { - if (primitives[0].attributes.texcoord[idx]->count != tval) - throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (", ai_to_string(tval), - ") not equal to uncompressed (", ai_to_string(primitives[0].attributes.texcoord[idx]->count), ")."); + case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD: + // Check situation when encoded data contain texture coordinates but primitive not. + if (idx_texcoord < primitives[0].attributes.texcoord.size()) { + if (primitives[0].attributes.texcoord[idx]->count != tval) + throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (", ai_to_string(tval), + ") not equal to uncompressed (", ai_to_string(primitives[0].attributes.texcoord[idx]->count), ")."); - idx_texcoord++; - } else { - ifs.SetNFloatAttribute(static_cast(idx), 0ul); // Disable decoding this attribute. - } + idx_texcoord++; + } else { + ifs.SetNFloatAttribute(static_cast(idx), 0ul); // Disable decoding this attribute. + } - break; - default: - throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: ", ai_to_string(ifs.GetFloatAttributeType(static_cast(idx)))); + break; + default: + throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: ", ai_to_string(ifs.GetFloatAttributeType(static_cast(idx)))); } tval *= ifs.GetFloatAttributeDim(static_cast(idx)) * sizeof(o3dgc::Real); // After checking count of objects we can get size of array. @@ -868,14 +867,14 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG // size = number_of_elements * components_per_element * size_of_component. See float attributes note. size_t tval = ifs.GetNIntAttribute(static_cast(idx)); switch (ifs.GetIntAttributeType(static_cast(idx))) { - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID: - break; + case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN: + case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX: + case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID: + case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID: + break; - default: - throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: ", ai_to_string(ifs.GetIntAttributeType(static_cast(idx)))); + default: + throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: ", ai_to_string(ifs.GetIntAttributeType(static_cast(idx)))); } tval *= ifs.GetIntAttributeDim(static_cast(idx)) * sizeof(long); // See float attributes note. @@ -901,30 +900,30 @@ inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DG for (size_t idx = 0, idx_end = size_floatattr.size(), idx_texcoord = 0; idx < idx_end; idx++) { switch (ifs.GetFloatAttributeType(static_cast(idx))) { - case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD: - if (idx_texcoord < primitives[0].attributes.texcoord.size()) { - // See above about absent attributes. - ifs.SetFloatAttribute(static_cast(idx), (o3dgc::Real *const)(decoded_data + get_buf_offset(primitives[0].attributes.texcoord[idx]))); - idx_texcoord++; - } + case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD: + if (idx_texcoord < primitives[0].attributes.texcoord.size()) { + // See above about absent attributes. + ifs.SetFloatAttribute(static_cast(idx), (o3dgc::Real *const)(decoded_data + get_buf_offset(primitives[0].attributes.texcoord[idx]))); + idx_texcoord++; + } - break; - default: - throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: ", ai_to_string(ifs.GetFloatAttributeType(static_cast(idx)))); + break; + default: + throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: ", ai_to_string(ifs.GetFloatAttributeType(static_cast(idx)))); } } for (size_t idx = 0, idx_end = size_intattr.size(); idx < idx_end; idx++) { switch (ifs.GetIntAttributeType(static_cast(idx))) { - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID: - case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID: - break; + case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN: + case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX: + case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID: + case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID: + break; - // ifs.SetIntAttribute(idx, (long* const)(decoded_data + get_buf_offset(primitives[0].attributes.joint))); - default: - throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: ", ai_to_string(ifs.GetIntAttributeType(static_cast(idx)))); + // ifs.SetIntAttribute(idx, (long* const)(decoded_data + get_buf_offset(primitives[0].attributes.joint))); + default: + throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: ", ai_to_string(ifs.GetIntAttributeType(static_cast(idx)))); } } @@ -1181,12 +1180,12 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) { // Smallest legal JSON file is "{}" Smallest loadable glTF file is larger than that but catch it later if (mSceneLength < 2) { - throw DeadlyImportError("GLTF: No JSON file contents"); + throw DeadlyImportError("GLTF: No JSON file contents"); } // Binary format only supports up to 4GB of JSON so limit it there to avoid extreme memory allocation if (mSceneLength > std::numeric_limits::max()) { - throw DeadlyImportError("GLTF: JSON size greater than 4GB"); + throw DeadlyImportError("GLTF: JSON size greater than 4GB"); } // read the scene data, ensure null termination @@ -1267,7 +1266,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { #undef CHECK_EXT } -inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool absolute) { +inline IOStream *Asset::OpenFile(const std::string &path, const char *mode, bool absolute) { #ifdef ASSIMP_API (void)absolute; return mIOSystem->Open(path, mode); @@ -1309,7 +1308,7 @@ inline std::string Asset::FindUniqueID(const std::string &str, const char *suffi } #if _MSC_VER -# pragma warning(pop) +#pragma warning(pop) #endif // _MSC_VER } // namespace glTF diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index d9d783e47..479749523 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -197,18 +197,18 @@ inline void SetDecodedIndexBuffer_Draco(const draco::Mesh &dracoMesh, Mesh::Prim // Not same size, convert switch (componentBytes) { - case sizeof(uint32_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); - break; - case sizeof(uint16_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); - break; - case sizeof(uint8_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); - break; - default: - ai_assert(false); - break; + case sizeof(uint32_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + break; + case sizeof(uint16_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + break; + case sizeof(uint8_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + break; + default: + ai_assert(false); + break; } // Assign this alternate data buffer to the accessor @@ -247,27 +247,27 @@ inline void SetDecodedAttributeBuffer_Draco(const draco::Mesh &dracoMesh, uint32 decodedAttribBuffer->Grow(dracoMesh.num_points() * pDracoAttribute->num_components() * componentBytes); switch (accessor.componentType) { - case ComponentType_BYTE: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - case ComponentType_UNSIGNED_BYTE: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - case ComponentType_SHORT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - case ComponentType_UNSIGNED_SHORT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - case ComponentType_UNSIGNED_INT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - case ComponentType_FLOAT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - default: - ai_assert(false); - break; + case ComponentType_BYTE: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + break; + case ComponentType_UNSIGNED_BYTE: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + break; + case ComponentType_SHORT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + break; + case ComponentType_UNSIGNED_SHORT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + break; + case ComponentType_UNSIGNED_INT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + break; + case ComponentType_FLOAT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + break; + default: + ai_assert(false); + break; } // Assign this alternate data buffer to the accessor @@ -299,7 +299,7 @@ inline LazyDict::~LazyDict() { template inline void LazyDict::AttachToDocument(Document &doc) { Value *container = nullptr; - const char* context = nullptr; + const char *context = nullptr; if (mExtId) { if (Value *exts = FindObject(doc, "extensions")) { @@ -721,18 +721,18 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) { while (pIndices != indicesEnd) { size_t offset; switch (indicesType) { - case ComponentType_UNSIGNED_BYTE: - offset = *pIndices; - break; - case ComponentType_UNSIGNED_SHORT: - offset = *reinterpret_cast(pIndices); - break; - case ComponentType_UNSIGNED_INT: - offset = *reinterpret_cast(pIndices); - break; - default: - // have fun with float and negative values from signed types as indices. - throw DeadlyImportError("Unsupported component type in index."); + case ComponentType_UNSIGNED_BYTE: + offset = *pIndices; + break; + case ComponentType_UNSIGNED_SHORT: + offset = *reinterpret_cast(pIndices); + break; + case ComponentType_UNSIGNED_INT: + offset = *reinterpret_cast(pIndices); + break; + default: + // have fun with float and negative values from signed types as indices. + throw DeadlyImportError("Unsupported component type in index."); } offset *= elementSize; @@ -751,9 +751,8 @@ inline void Accessor::Read(Value &obj, Asset &r) { byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0)); componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); { - const Value* countValue = FindUInt(obj, "count"); - if (!countValue) - { + const Value *countValue = FindUInt(obj, "count"); + if (!countValue) { throw DeadlyImportError("A count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); } count = countValue->GetUint(); @@ -1233,7 +1232,7 @@ inline void Material::Read(Value &material, Asset &r) { MaterialIOR ior; ReadMember(*curMaterialIOR, "ior", ior.ior); - + this->materialIOR = Nullable(ior); } } @@ -1835,7 +1834,7 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) { } else { mSceneLength = stream->FileSize(); mBodyLength = 0; - + // Binary format only supports up to 4GB of JSON, use that as a maximum if (mSceneLength > std::numeric_limits::max()) { throw DeadlyImportError("GLTF: JSON size greater than 4GB"); @@ -1852,7 +1851,7 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) { // Smallest legal JSON file is "{}" Smallest loadable glTF file is larger than that but catch it later if (mSceneLength < 2) { - throw DeadlyImportError("GLTF: No JSON file contents"); + throw DeadlyImportError("GLTF: No JSON file contents"); } // parse the JSON document @@ -1983,7 +1982,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { #undef CHECK_EXT } -inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool /*absolute*/) { +inline IOStream *Asset::OpenFile(const std::string &path, const char *mode, bool /*absolute*/) { #ifdef ASSIMP_API return mIOSystem->Open(path, mode); #else From aaae3e3a10902cd3c3eec18e18233ac6b6f77185 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Mon, 8 Nov 2021 15:05:20 +0000 Subject: [PATCH 3/3] size_t is 32bit on some platforms Also assert if size_t is smaller than uint32_t (probably not necessary) Note: 32bit builds will crash OOM if a really large model is loaded, as cannot allocate that much in total, let alone contiguously. --- code/AssetLib/glTF/glTFAsset.inl | 3 ++- code/AssetLib/glTF2/glTF2Asset.inl | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/glTF/glTFAsset.inl b/code/AssetLib/glTF/glTFAsset.inl index 896a9f16c..10be7c78e 100644 --- a/code/AssetLib/glTF/glTFAsset.inl +++ b/code/AssetLib/glTF/glTFAsset.inl @@ -1147,6 +1147,7 @@ inline void Asset::ReadBinaryHeader(IOStream &stream) { AI_SWAP4(header.length); AI_SWAP4(header.sceneLength); + static_assert(std::numeric_limits::max() <= std::numeric_limits::max(), "size_t must be at least 32bits"); mSceneLength = static_cast(header.sceneLength); // Can't be larger than 4GB (max. uint32_t) mBodyOffset = sizeof(header) + mSceneLength; @@ -1184,7 +1185,7 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) { } // Binary format only supports up to 4GB of JSON so limit it there to avoid extreme memory allocation - if (mSceneLength > std::numeric_limits::max()) { + if (mSceneLength >= std::numeric_limits::max()) { throw DeadlyImportError("GLTF: JSON size greater than 4GB"); } diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 479749523..3fc238208 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1777,7 +1777,7 @@ inline void Asset::ReadBinaryHeader(IOStream &stream, std::vector &sceneDa } // read the scene data, ensure null termination - + static_assert(std::numeric_limits::max() <= std::numeric_limits::max(), "size_t must be at least 32bits"); mSceneLength = chunk.chunkLength; // Can't be larger than 4GB (max. uint32_t) sceneData.resize(mSceneLength + 1); sceneData[mSceneLength] = '\0'; @@ -1836,7 +1836,7 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) { mBodyLength = 0; // Binary format only supports up to 4GB of JSON, use that as a maximum - if (mSceneLength > std::numeric_limits::max()) { + if (mSceneLength >= std::numeric_limits::max()) { throw DeadlyImportError("GLTF: JSON size greater than 4GB"); }