diff --git a/include/flatbuffers/verifier.h b/include/flatbuffers/verifier.h index a0b793597c..968c13b554 100644 --- a/include/flatbuffers/verifier.h +++ b/include/flatbuffers/verifier.h @@ -255,9 +255,9 @@ class VerifierTemplate FLATBUFFERS_FINAL_CLASS { template bool VerifySizePrefixedBuffer(const char* const identifier) { return Verify(0U) && - // Ensure the prefixed size is within the bounds of the provided - // length. - Check(ReadScalar(buf_) + sizeof(SizeT) <= size_) && + // The declared payload size must fit in the bytes remaining after + // the size prefix, and subtraction avoids unsigned wraparound. + Check(ReadScalar(buf_) <= size_ - sizeof(SizeT)) && VerifyBufferFromStart(identifier, sizeof(SizeT)); } diff --git a/tests/monster_test.cpp b/tests/monster_test.cpp index 9b188afa24..a01768607e 100644 --- a/tests/monster_test.cpp +++ b/tests/monster_test.cpp @@ -1,5 +1,7 @@ #include "monster_test.h" +#include +#include #include #include @@ -633,6 +635,35 @@ void SizePrefixedTest() { fbb.GetSize() - 10); TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier_smaller), false); } + + { + std::vector corrupted(fbb.GetBufferPointer(), + fbb.GetBufferPointer() + fbb.GetSize()); + const size_t buffer_size = corrupted.size(); + // Mutate only the prefix. Payload remains valid. + const uoffset_t invalid_prefix = + buffer_size < static_cast(std::numeric_limits::max()) + ? static_cast(buffer_size) + static_cast(1) + : std::numeric_limits::max(); + std::memcpy(corrupted.data(), &invalid_prefix, sizeof(invalid_prefix)); + + flatbuffers::Verifier verifier_invalid(corrupted.data(), corrupted.size()); + // The declared payload size must not exceed the bytes after the prefix. + TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier_invalid), false); + } + +#if UINTPTR_MAX == 0xffffffffu + { + std::vector corrupted(fbb.GetBufferPointer(), + fbb.GetBufferPointer() + fbb.GetSize()); + const uoffset_t overflow_prefix = std::numeric_limits::max(); + std::memcpy(corrupted.data(), &overflow_prefix, sizeof(overflow_prefix)); + + flatbuffers::Verifier verifier_overflow(corrupted.data(), corrupted.size()); + // On 32-bit builds, this documents the old wraparound behavior. + TEST_EQ(VerifySizePrefixedMonsterBuffer(verifier_overflow), false); + } +#endif } void TestMonsterExtraFloats(const std::string& tests_data_path) {