Compare commits

..

22 Commits

Author SHA1 Message Date
Syoyo Fujita
e0b625561c v2.6.3 2022-09-19 03:36:58 +09:00
Syoyo Fujita
18450eafe7 Merge pull request #382 from syoyo/glb-zero-chunk
Fix parsing GLB file with empty Chunk1(BIN data).
2022-09-19 03:34:09 +09:00
Syoyo Fujita
e9fbc03e2d Clear error/warn message. 2022-09-19 03:29:57 +09:00
Syoyo Fujita
612e57816f Fix handling <4 byte BIN data.
Fix handling GLB file with empty CHUNK1(BIN).
2022-09-18 21:01:39 +09:00
Kh4n
a778c089d1 readd toplevel makefile 2022-09-17 15:39:28 -05:00
Kh4n
387fd61b83 update test to match gltf-validator 2022-09-17 13:02:39 -05:00
Kh4n
6514490090 update gitignore to remove test file
readd accidental removals in gitignore

undo autoformat

more undo autoformatting
2022-09-17 12:52:59 -05:00
Kh4n
6b7ec9f494 added tests to cover empty, empty buffer, and single byte buffer cases 2022-09-17 12:28:39 -05:00
Syoyo Fujita
c670f08a3b Fix parsing GLB file with empty Chunk1(BIN data). 2022-09-17 19:52:25 +09:00
Syoyo Fujita
eec4c98862 Add note on v2.6.2(Fix out-of-bounds access of accessors. PR#379) 2022-09-16 17:27:20 +09:00
Syoyo Fujita
c7e911cf11 Merge pull request #380 from AlvaroBarua/master
Fixes compiler warning on VS (Unreachable code detected)
2022-09-11 17:56:55 +09:00
AlvaroBarua
43172238f7 Fixes compiler warning on VS (Unreachable code detected) 2022-09-11 00:41:43 +01:00
Syoyo Fujita
0cc23356dc Merge pull request #379 from nirmal/patch-1
Fix possible out of bounds index in LoadFromString
2022-09-07 01:52:55 +09:00
Nirmal Patel
e413216722 Fix possible out of bounds index in LoadFromString 2022-09-06 09:16:31 -07:00
Syoyo Fujita
4581d37bec v2.6.1 2022-09-06 22:02:31 +09:00
Syoyo Fujita
966a9d0df7 Merge pull request #374 from syoyo/glb_chunk_check
Better GLB data size check when reading.
2022-09-06 21:35:47 +09:00
Syoyo Fujita
64452bb5fa Merge pull request #377 from zbendefy/master
Auto detect C++14 standard version
2022-09-06 14:01:26 +09:00
zbendefy
69eeea145b Auto detect C++14 standard version 2022-09-05 23:54:57 +02:00
Syoyo Fujita
24e539621d Merge pull request #376 from kacprzak/master
Read from moved object
2022-09-03 01:16:41 +09:00
Marcin Kacprzak
f4f5c3cf3a Fix read from moved object. 2022-09-02 16:15:54 +02:00
Marcin Kacprzak
b12a54ed15 Silence MS code analysis tool. 2022-09-02 16:13:11 +02:00
Syoyo Fujita
240d993f94 Create codeql-analysis.yml 2022-09-02 05:09:09 +09:00
4 changed files with 228 additions and 56 deletions

72
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
schedule:
- cron: '21 20 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

7
.gitignore vendored
View File

@@ -68,4 +68,11 @@ loader_example
tests/tester
tests/tester_noexcept
tests/issue-97.gltf
tests/issue-261.gltf
# unignore
!Makefile
!examples/build-gltf/Makefile
!examples/raytrace/cornellbox_suzanne.obj
!tests/Makefile
!tools/windows/premake5.exe

View File

@@ -482,3 +482,61 @@ TEST_CASE("expandpath-utf-8", "[pr-226]") {
}
#endif
TEST_CASE("empty-bin-buffer", "[issue-382]") {
tinygltf::Model model;
tinygltf::TinyGLTF ctx;
std::string err;
std::string warn;
tinygltf::Model model_empty;
std::stringstream stream;
bool ret = ctx.WriteGltfSceneToStream(&model_empty, stream, false, true);
REQUIRE(ret == true);
std::string str = stream.str();
const unsigned char* bytes = (unsigned char*)str.data();
ret = ctx.LoadBinaryFromMemory(&model, &err, &warn, bytes, str.size());
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
err.clear();
warn.clear();
tinygltf::Model model_empty_buffer;
model_empty_buffer.buffers.push_back(tinygltf::Buffer());
stream = std::stringstream();
ret = ctx.WriteGltfSceneToStream(&model_empty_buffer, stream, false, true);
REQUIRE(ret == true);
str = stream.str();
bytes = (unsigned char*)str.data();
ret = ctx.LoadBinaryFromMemory(&model, &err, &warn, bytes, str.size());
if (err.empty()) {
std::cerr << "there should have been an error reported" << std::endl;
}
REQUIRE(false == ret);
err.clear();
warn.clear();
tinygltf::Model model_single_byte_buffer;
tinygltf::Buffer buffer;
buffer.data.push_back(0);
model_single_byte_buffer.buffers.push_back(buffer);
stream = std::stringstream();
ret = ctx.WriteGltfSceneToStream(&model_single_byte_buffer, stream, false, true);
REQUIRE(ret == true);
str = stream.str();
{
std::ofstream ofs("tmp.glb");
ofs.write(str.data(), str.size());
}
bytes = (unsigned char*)str.data();
ret = ctx.LoadBinaryFromMemory(&model_single_byte_buffer, &err, &warn, bytes, str.size());
if (!err.empty()) {
std::cerr << err << std::endl;
}
REQUIRE(true == ret);
}

View File

@@ -26,6 +26,9 @@
// THE SOFTWARE.
// Version:
// - v2.6.3 Fix GLB file with empty BIN chunk was not handled. PR#382 and PR#383.
// - v2.6.2 Fix out-of-bounds access of accessors. PR#379.
// - v2.6.1 Better GLB validation check when loading.
// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv).
// Disable expanding file path for security(no use of awkward `wordexp` anymore).
// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
@@ -66,6 +69,11 @@
#include <string>
#include <vector>
//Auto-detect C++14 standard version
#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L)
#define TINYGLTF_USE_CPP14
#endif
#ifndef TINYGLTF_USE_CPP14
#include <functional>
#endif
@@ -3229,9 +3237,11 @@ static bool ParseJsonAsValue(Value *ret, const json &o) {
break;
}
#endif
const bool isNotNull = val.Type() != NULL_TYPE;
if (ret) *ret = std::move(val);
return val.Type() != NULL_TYPE;
return isNotNull;
}
static bool ParseExtrasProperty(Value *ret, const json &o) {
@@ -3671,7 +3681,8 @@ static bool ParseParameterProperty(Parameter *param, std::string *err,
// Found a number array.
return true;
} else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
return param->has_number_value = true;
param->has_number_value = true;
return true;
} else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
false)) {
return true;
@@ -3864,7 +3875,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
image->uri = uri;
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
return true;
#endif
#else
std::string decoded_uri = dlib::urldecode(uri);
if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
/* required */ false, /* required bytes */ 0,
@@ -3886,6 +3897,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err,
}
return false;
}
#endif
}
if (*LoadImageData == nullptr) {
@@ -4102,7 +4114,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
if ((bin_size == 0) || (bin_data == nullptr)) {
if (err) {
(*err) += "Invalid binary data in `Buffer'.\n";
(*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
}
return false;
}
@@ -5794,20 +5806,27 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
}
for (auto &attribute : primitive.attributes) {
model
->bufferViews[size_t(
model->accessors[size_t(attribute.second)].bufferView)]
.target = TINYGLTF_TARGET_ARRAY_BUFFER;
const auto accessorsIndex = size_t(attribute.second);
if (accessorsIndex < model->accessors.size()) {
const auto bufferView = model->accessors[accessorsIndex].bufferView;
// bufferView could be null(-1) for sparse morph target
if (bufferView >= 0 && bufferView < model->bufferViews.size()) {
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ARRAY_BUFFER;
}
}
}
for (auto &target : primitive.targets) {
for (auto &attribute : target) {
auto bufferView =
model->accessors[size_t(attribute.second)].bufferView;
// bufferView could be null(-1) for sparse morph target
if (bufferView >= 0) {
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ARRAY_BUFFER;
const auto accessorsIndex = size_t(attribute.second);
if (accessorsIndex < model->accessors.size()) {
const auto bufferView = model->accessors[accessorsIndex].bufferView;
// bufferView could be null(-1) for sparse morph target
if (bufferView >= 0 && bufferView < model->bufferViews.size()) {
model->bufferViews[size_t(bufferView)].target =
TINYGLTF_TARGET_ARRAY_BUFFER;
}
}
}
}
@@ -6268,15 +6287,15 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
//
// https://github.com/syoyo/tinygltf/issues/372
// Use 64bit uint to avoid integer overflow.
uint64_t json_size = 20ull + uint64_t(chunk0_length);
uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
if (json_size > std::numeric_limits<uint32_t>::max()) {
if (header_and_json_size > std::numeric_limits<uint32_t>::max()) {
// Do not allow 4GB or more GLB data.
(*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
}
if ((json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
(json_size > uint64_t(length)) ||
if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
(header_and_json_size > uint64_t(length)) ||
(chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
if (err) {
(*err) = "Invalid glTF binary.";
@@ -6287,61 +6306,77 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
// Padding check
// The start and the end of each chunk must be aligned to a 4-byte boundary.
// No padding check for chunk0 start since its 4byte-boundary is ensured.
if ((json_size % 4) != 0) {
if ((header_and_json_size % 4) != 0) {
if (err) {
(*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
}
}
// Read Chunk1 info(BIN data)
if ((json_size + 8ull) > uint64_t(length)) {
if (err) {
(*err) = "Insufficient storage space for Chunk1(BIN data).";
//std::cout << "header_and_json_size = " << header_and_json_size << "\n";
//std::cout << "length = " << length << "\n";
// Chunk1(BIN) data
// The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted.
// So when header + JSON data == binary size, Chunk1 is omitted.
if (header_and_json_size == uint64_t(length)) {
bin_data_ = nullptr;
bin_size_ = 0;
} else {
// Read Chunk1 info(BIN data)
// At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aliged to 4 bytes)
if ((header_and_json_size + 12ull) > uint64_t(length)) {
if (err) {
(*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n";
}
return false;
}
return false;
}
unsigned int chunk1_length; // 4 bytes
unsigned int chunk1_format; // 4 bytes;
memcpy(&chunk1_length, bytes + json_size, 4); // JSON data length
swap4(&chunk1_length);
memcpy(&chunk1_format, bytes + json_size + 4, 4);
swap4(&chunk1_format);
unsigned int chunk1_length; // 4 bytes
unsigned int chunk1_format; // 4 bytes;
memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length
swap4(&chunk1_length);
memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
swap4(&chunk1_format);
if (chunk1_length < 4) {
// TODO: Do we allow 0byte BIN data?
if (err) {
(*err) = "Insufficient Chunk1(BIN) data size.";
//std::cout << "chunk1_length = " << chunk1_length << "\n";
if (chunk1_length < 4) {
if (err) {
(*err) = "Insufficient Chunk1(BIN) data size.";
}
return false;
}
return false;
}
if ((chunk1_length % 4) != 0) {
if (err) {
(*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
if ((chunk1_length % 4) != 0) {
if (err) {
(*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
}
return false;
}
return false;
}
if (uint64_t(chunk1_length) + json_size > uint64_t(length)) {
if (err) {
(*err) = "BIN Chunk data length exceeds the GLB size.";
if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
if (err) {
(*err) = "BIN Chunk data length exceeds the GLB size.";
}
return false;
}
return false;
}
if (chunk1_format != 0x004e4942) {
if (err) {
(*err) = "Invlid type for chunk1 data.";
if (chunk1_format != 0x004e4942) {
if (err) {
(*err) = "Invlid type for chunk1 data.";
}
return false;
}
return false;
//std::cout << "chunk1_length = " << chunk1_length << "\n";
bin_data_ = bytes + header_and_json_size +
8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
bin_size_ = size_t(chunk1_length);
}
bin_data_ = bytes + json_size +
8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
bin_size_ = size_t(chunk1_length);
// Extract JSON string.
std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
chunk0_length);