Skip to content

Commit

Permalink
Allow zero-sized BIN chunk. Fixes #440
Browse files Browse the repository at this point in the history
  • Loading branch information
syoyo committed Jan 23, 2024
1 parent c5641f2 commit 4fea26f
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 33 deletions.
Binary file added models/regression/zero-sized-bin-chunk-issue-440.glb
Binary file not shown.
24 changes: 24 additions & 0 deletions tests/tester.cc
Original file line number Diff line number Diff line change
Expand Up @@ -902,3 +902,27 @@ TEST_CASE("serialize-empty-scene", "[issue-464]") {
CHECK(m.scenes[0] == scene);
}
}

TEST_CASE("zero-sized-bin-chunk-glb", "[issue-440]") {

tinygltf::Model model;
tinygltf::TinyGLTF ctx;
std::string err;
std::string warn;

// Input glb has zero-sized data in bin chunk(8 bytes for BIN chunk, and chunksize == 0)
// The spec https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-buffer says
//
// When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted.
//
// 'SHOULD' mean 'RECOMMENDED', so we'll need to allow such zero-sized bin chunk is NOT omitted.
bool ret = ctx.LoadBinaryFromFile(&model, &err, &warn, "../models/regression/zero-sized-bin-chunk-issue-440.glb");
if (!warn.empty()) {
std::cout << "WARN: " << warn << "\n";
}
if (!err.empty()) {
std::cerr << err << std::endl;
}

REQUIRE(true == ret);
}
84 changes: 51 additions & 33 deletions tiny_gltf.h
Original file line number Diff line number Diff line change
Expand Up @@ -6652,7 +6652,7 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,

memcpy(&version, bytes + 4, 4);
swap4(&version);
memcpy(&length, bytes + 8, 4);
memcpy(&length, bytes + 8, 4); // Total glb size, including header and all chunks.
swap4(&length);
memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
swap4(&chunk0_length);
Expand Down Expand Up @@ -6708,68 +6708,86 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
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 aligned to 4 bytes)
if ((header_and_json_size + 12ull) > uint64_t(length)) {
//
// issue-440:
// 'SHOULD' in glTF spec means 'RECOMMENDED',
// So there is a situation that Chunk1(BIN) is composed of zero-sized BIN data
// (chunksize(0) + binformat(BIN) = 8bytes).
//
if ((header_and_json_size + 8ull) > uint64_t(length)) {
if (err) {
(*err) =
"Insufficient storage space for Chunk1(BIN data). At least Chunk1 "
"Must have 4 or more bytes, but got " +
"Must have 8 or more bytes, but got " +
std::to_string((header_and_json_size + 8ull) - uint64_t(length)) +
".\n";
}
return false;
}

unsigned int chunk1_length; // 4 bytes
unsigned int chunk1_format; // 4 bytes;
unsigned int chunk1_length{0}; // 4 bytes
unsigned int chunk1_format{0}; // 4 bytes;
memcpy(&chunk1_length, bytes + header_and_json_size,
4); // JSON data length
4); // Bin data length
swap4(&chunk1_length);
memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
swap4(&chunk1_format);

// std::cout << "chunk1_length = " << chunk1_length << "\n";

if (chunk1_length < 4) {
if (chunk1_format != 0x004e4942) {
if (err) {
(*err) = "Insufficient Chunk1(BIN) data size.";
(*err) = "Invalid chunkType for Chunk1.";
}
return false;
}

if ((chunk1_length % 4) != 0) {
if (strictness_==ParseStrictness::Permissive) {
if (warn) {
(*warn) += "BIN Chunk end is not aligned to a 4-byte boundary.\n";
if (chunk1_length == 0) {

if (header_and_json_size + 8 > uint64_t(length)) {
if (err) {
(*err) = "BIN Chunk header location exceeds the GLB size.";
}
return false;
}
else {

bin_data_ = nullptr;

} else {

// When BIN chunk size is not zero, at least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin
// payload could be 1~3 bytes, but need to be aligned to 4 bytes)

if (chunk1_length < 4) {
if (err) {
(*err) = "BIN Chunk end is not aligned to a 4-byte boundary.";
(*err) = "Insufficient Chunk1(BIN) data size.";
}
return false;
}
}

if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
if (err) {
(*err) = "BIN Chunk data length exceeds the GLB size.";
if ((chunk1_length % 4) != 0) {
if (strictness_==ParseStrictness::Permissive) {
if (warn) {
(*warn) += "BIN Chunk end is not aligned to a 4-byte boundary.\n";
}
}
else {
if (err) {
(*err) = "BIN Chunk end is not aligned to a 4-byte boundary.";
}
return false;
}
}
return false;
}

if (chunk1_format != 0x004e4942) {
if (err) {
(*err) = "Invalid type for chunk1 data.";
// +8 chunk1 header size.
if (uint64_t(chunk1_length) + header_and_json_size + 8 > uint64_t(length)) {
if (err) {
(*err) = "BIN Chunk data length exceeds the GLB size.";
}
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_data_ = bytes + header_and_json_size +
8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
}

bin_size_ = size_t(chunk1_length);
}
Expand Down

0 comments on commit 4fea26f

Please sign in to comment.