浏览代码

Merge pull request #3514 from ms-maxvollmer/gltf_fixes

GLTF: Fix crash on invalid base64 data + improved error messages
Kim Kulling 4 年之前
父节点
当前提交
aee01e8345

+ 3 - 1
code/AssetLib/glTF/glTFCommon.cpp

@@ -49,7 +49,9 @@ using namespace glTFCommon::Util;
 namespace Util {
 
 size_t DecodeBase64(const char *in, size_t inLength, uint8_t *&out) {
-    ai_assert(inLength % 4 == 0);
+    if (inLength % 4 != 0) {
+        throw DeadlyImportError("Invalid base64 encoded data: \"", std::string(in, std::min(size_t(32), inLength)), "\", length:", inLength);
+    }
 
     if (inLength < 4) {
         out = 0;

+ 4 - 1
code/AssetLib/glTF/glTFCommon.h

@@ -249,7 +249,10 @@ inline char EncodeCharBase64(uint8_t b) {
 }
 
 inline uint8_t DecodeCharBase64(char c) {
-    return DATA<true>::tableDecodeBase64[size_t(c)]; // TODO faster with lookup table or ifs?
+    if (c & 0x80) {
+        throw DeadlyImportError("Invalid base64 char value: ", size_t(c));
+    }
+    return DATA<true>::tableDecodeBase64[size_t(c & 0x7F)]; // TODO faster with lookup table or ifs?
 }
 
 size_t DecodeBase64(const char *in, size_t inLength, uint8_t *&out);

+ 8 - 0
code/AssetLib/glTF2/glTF2Asset.h

@@ -1124,6 +1124,14 @@ private:
     IOStream *OpenFile(std::string path, const char *mode, bool absolute = false);
 };
 
+inline std::string getContextForErrorMessages(const std::string &id, const std::string &name) {
+    std::string context = id;
+    if (!name.empty()) {
+        context += " (\"" + name + "\")";
+    }
+    return context;
+}
+
 } // namespace glTF2
 
 // Include the implementation of the methods

+ 11 - 14
code/AssetLib/glTF2/glTF2Asset.inl

@@ -273,17 +273,21 @@ Ref<T> LazyDict<T>::Retrieve(unsigned int i) {
     }
 
     if (!mDict->IsArray()) {
-        throw DeadlyImportError("GLTF: Field is not an array \"", mDictId, "\"");
+        throw DeadlyImportError("GLTF: Field \"", mDictId, "\"  is not an array");
+    }
+
+    if (i >= mDict->Size()) {
+        throw DeadlyImportError("GLTF: Array index ", i, " is out of bounds (", mDict->Size(), ") for \"", mDictId, "\"");
     }
 
     Value &obj = (*mDict)[i];
 
     if (!obj.IsObject()) {
-        throw DeadlyImportError("GLTF: Object at index \"", to_string(i), "\" is not a JSON object");
+        throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" is not a JSON object");
     }
 
     if (mRecursiveReferenceCheck.find(i) != mRecursiveReferenceCheck.end()) {
-        throw DeadlyImportError("GLTF: Object at index \"", to_string(i), "\" has recursive reference to itself");
+        throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" has recursive reference to itself");
     }
     mRecursiveReferenceCheck.insert(i);
 
@@ -741,14 +745,6 @@ inline void CopyData(size_t count,
     }
 }
 
-inline std::string getContextForErrorMessages(const std::string& id, const std::string& name) {
-    std::string context = id;
-    if (!name.empty()) {
-        context += " (\"" + name + "\")";
-    }
-    return context;
-}
-
 } // namespace
 
 template <class T>
@@ -766,11 +762,12 @@ void Accessor::ExtractData(T *&outData) {
     const size_t targetElemSize = sizeof(T);
 
     if (elemSize > targetElemSize) {
-        throw DeadlyImportError("GLTF: elemSize > targetElemSize");
+        throw DeadlyImportError("GLTF: elemSize ", elemSize, " > targetElemSize ", targetElemSize, " in ", getContextForErrorMessages(id, name));
     }
 
-    if (count*stride > (bufferView ? bufferView->byteLength : sparse->data.size())) {
-        throw DeadlyImportError("GLTF: count*stride out of range");
+    const size_t maxSize = (bufferView ? bufferView->byteLength : sparse->data.size());
+    if (count*stride > maxSize) {
+        throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
     }
 
     outData = new T[count];

+ 4 - 1
code/AssetLib/glTF2/glTF2Importer.cpp

@@ -918,7 +918,10 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
 
     if (!node.meshes.empty()) {
         // GLTF files contain at most 1 mesh per node.
-        assert(node.meshes.size() == 1);
+        if (node.meshes.size() > 1)
+        {
+            throw DeadlyImportError("GLTF: Invalid input, found ", node.meshes.size(), " meshes in ", getContextForErrorMessages(node.id, node.name), ", but only 1 mesh per node allowed.");
+        }
         int mesh_idx = node.meshes[0].GetIndex();
         int count = meshOffsets[mesh_idx + 1] - meshOffsets[mesh_idx];