Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion include/flatbuffers/flatbuffers.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,20 @@ inline SizeT GetPrefixedSize(const uint8_t* buf) {
return ReadScalar<SizeT>(buf);
}

// Computes the total length of a size-prefixed FlatBuffer and returns false
// if the addition would overflow SizeT.
template <typename SizeT = uoffset_t>
inline bool TryGetSizePrefixedBufferLength(const uint8_t* const buf,
SizeT* const length) {
const auto prefix = ReadScalar<SizeT>(buf);
const auto prefix_field_size = static_cast<SizeT>(sizeof(SizeT));
if (prefix > (std::numeric_limits<SizeT>::max)() - prefix_field_size) {
return false;
}
*length = static_cast<SizeT>(prefix + prefix_field_size);
return true;
}

// Gets the total length of the buffer given a sized prefixed FlatBuffer.
//
// This includes the size of the prefix as well as the buffer:
Expand All @@ -89,7 +103,12 @@ inline SizeT GetPrefixedSize(const uint8_t* buf) {
// |---------length--------|
template <typename SizeT = uoffset_t>
inline SizeT GetSizePrefixedBufferLength(const uint8_t* const buf) {
return ReadScalar<SizeT>(buf) + sizeof(SizeT);
SizeT length = 0;
if (!TryGetSizePrefixedBufferLength<SizeT>(buf, &length)) {
FLATBUFFERS_ASSERT(false);
return 0;
}
return length;
}

// Base class for native objects (FlatBuffer data de-serialized into native
Expand Down
34 changes: 34 additions & 0 deletions tests/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,39 @@ void NestedVerifierTest() {
}
}

void SizePrefixedBufferLengthOverflowTest() {
const uint8_t crafted[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // size prefix
0x00, 0x00, 0x00, 0x00,
};

const auto prefix = (std::numeric_limits<flatbuffers::uoffset64_t>::max)();
const auto prefix_size =
static_cast<flatbuffers::uoffset64_t>(sizeof(flatbuffers::uoffset64_t));
const auto legacy_wrapped =
static_cast<flatbuffers::uoffset64_t>(prefix + prefix_size);
TEST_OUTPUT_LINE(
"Overflow proof (legacy math): prefix=%llu + sizeof=%llu => wrapped=%llu",
static_cast<unsigned long long>(prefix),
static_cast<unsigned long long>(prefix_size),
static_cast<unsigned long long>(legacy_wrapped));
TEST_EQ(legacy_wrapped, static_cast<flatbuffers::uoffset64_t>(7));

flatbuffers::uoffset64_t length = 123;
const bool ok = flatbuffers::TryGetSizePrefixedBufferLength<
flatbuffers::uoffset64_t>(crafted, &length);
TEST_OUTPUT_LINE(
"Overflow proof (checked API): ok=%d, out_length=%llu (input preserved on failure)",
ok ? 1 : 0, static_cast<unsigned long long>(length));
TEST_EQ(ok, false);
TEST_EQ(length, static_cast<flatbuffers::uoffset64_t>(123));

// Legacy helper now safely returns 0 on overflow in non-assert builds.
TEST_EQ(flatbuffers::GetSizePrefixedBufferLength<flatbuffers::uoffset64_t>(
crafted),
static_cast<flatbuffers::uoffset64_t>(0));
}

void SizeVerifierTest() {
// Create a monster.
flatbuffers::FlatBufferBuilder builder;
Expand Down Expand Up @@ -1849,6 +1882,7 @@ int FlatBufferTests(const std::string& tests_data_path) {
FlatbuffersIteratorsTest();
WarningsAsErrorsTest();
NestedVerifierTest();
SizePrefixedBufferLengthOverflowTest();
SizeVerifierTest();
PrivateAnnotationsLeaks();
JsonUnsortedArrayTest();
Expand Down