diff --git a/Engine/Resource/binary_bundle.cpp b/Engine/Resource/binary_bundle.cpp index d6a6c8c..b199ce0 100644 --- a/Engine/Resource/binary_bundle.cpp +++ b/Engine/Resource/binary_bundle.cpp @@ -167,6 +167,8 @@ BinaryBundle read_binary_bundle(const std::span data) { const auto metadataOffset = reader.u32(endian); const auto metadataSize = reader.u32(endian); if (strings > data.size()) throw std::out_of_range("Binary bundle string table is out of range"); + const auto tables = std::uint64_t{ebxCount} * 28 + std::uint64_t{resourceCount} * 56 + std::uint64_t{chunkCount} * 44; // SHA-1 plus entry bytes + if (tables > reader.remaining()) throw std::out_of_range("Binary bundle asset counts exceed the file"); std::vector hashes(total); for (auto& hash : hashes) hash = reader.sha1(); diff --git a/Engine/Resource/toc.cpp b/Engine/Resource/toc.cpp index d5479ca..93917b4 100644 --- a/Engine/Resource/toc.cpp +++ b/Engine/Resource/toc.cpp @@ -133,6 +133,10 @@ std::string c_string_at(const std::span data, const std::size_t throw std::runtime_error("TOC name is unterminated"); } +void expect_table(const BinaryReader& reader, std::size_t count, std::size_t entrySize, const char* what) { // before sizing by a file's count + if (count > reader.remaining() / entrySize) throw std::out_of_range(what); +} + class HuffmanNames final { public: HuffmanNames(const std::span data, @@ -142,6 +146,7 @@ public: const std::size_t tableCount) { BinaryReader reader(data); reader.seek(namesOffset); + expect_table(reader, namesCount, 4, "TOC name count exceeds the file"); words_.reserve(namesCount); for (std::size_t index = 0; index < namesCount; ++index) words_.push_back(reader.u32(Endian::big)); reader.seek(tableOffset); @@ -364,9 +369,11 @@ TocDocument read_toc(const std::span data, TocDocument result; result.flags = flags; reader.seek(bundleHashOffset); + expect_table(reader, bundlesSize, 4, "TOC bundle count exceeds the file"); result.bundleHashMap.reserve(bundlesSize); for (std::size_t index = 0; index < bundlesSize; ++index) result.bundleHashMap.push_back(reader.i32(Endian::big)); reader.seek(bundleDataOffset); + expect_table(reader, bundlesSize, 16, "TOC bundle count exceeds the file"); result.bundles.reserve(bundlesSize); for (std::size_t index = 0; index < bundlesSize; ++index) { const auto nameOffset = reader.u32(Endian::big); @@ -389,12 +396,15 @@ TocDocument read_toc(const std::span data, } if (chunksSize) { reader.seek(chunkHashOffset); + expect_table(reader, chunksSize, 4, "TOC chunk count exceeds the file"); result.chunkHashMap.reserve(chunksSize); for (std::size_t index = 0; index < chunksSize; ++index) result.chunkHashMap.push_back(reader.i32(Endian::big)); - std::vector words(static_cast(dataCount)); reader.seek(chunkDataOffset); + expect_table(reader, static_cast(dataCount), 4, "TOC chunk data count exceeds the file"); + std::vector words(static_cast(dataCount)); for (auto& value : words) value = reader.u32(Endian::big); reader.seek(chunkGuidOffset); + expect_table(reader, chunksSize, 20, "TOC chunk count exceeds the file"); result.chunks.reserve(chunksSize); for (std::size_t index = 0; index < chunksSize; ++index) { const auto guidBytes = reader.view(16);