diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index 0d9b5ccedb4..5217046f756 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -75,6 +75,85 @@ inline const uint8_t* GetBufferStartFromRootPointer(const void* root) { return nullptr; } +// Checked variants of the root access helpers that first run the FlatBuffers +// verifier on the provided buffer. These are intended as secure-by-default +// helpers for callers that are dealing with untrusted input and want a single +// API that combines verification with obtaining the typed root pointer. +// +// If verification fails, these functions return nullptr instead of a typed +// pointer into the buffer. +template +inline const T* GetRootChecked( + const void* buf, size_t len, + const Verifier::Options& opts = Verifier::Options()) { + if (!buf) return nullptr; + Verifier verifier(reinterpret_cast(buf), len, opts); + if (!verifier.VerifyBuffer()) return nullptr; + return GetRoot(buf); +} + +template +inline const T* GetRootChecked( + const void* buf, size_t len, const char* identifier, + const Verifier::Options& opts = Verifier::Options()) { + if (!buf) return nullptr; + Verifier verifier(reinterpret_cast(buf), len, opts); + if (!verifier.VerifyBuffer(identifier)) return nullptr; + return GetRoot(buf); +} + +template +inline const T* GetSizePrefixedRootChecked( + const void* buf, size_t len, + const Verifier::Options& opts = Verifier::Options()) { + if (!buf) return nullptr; + Verifier verifier(reinterpret_cast(buf), len, opts); + if (!verifier.VerifySizePrefixedBuffer(nullptr)) return nullptr; + return GetSizePrefixedRoot(buf); +} + +template +inline const T* GetSizePrefixedRootChecked( + const void* buf, size_t len, const char* identifier, + const Verifier::Options& opts = Verifier::Options()) { + if (!buf) return nullptr; + Verifier verifier(reinterpret_cast(buf), len, opts); + if (!verifier.VerifySizePrefixedBuffer(identifier)) return nullptr; + return GetSizePrefixedRoot(buf); +} + +template +inline T* GetMutableRootChecked( + void* buf, size_t len, + const Verifier::Options& opts = Verifier::Options()) { + return const_cast( + GetRootChecked(const_cast(buf), len, opts)); +} + +template +inline T* GetMutableRootChecked( + void* buf, size_t len, const char* identifier, + const Verifier::Options& opts = Verifier::Options()) { + return const_cast(GetRootChecked(const_cast(buf), len, + identifier, opts)); +} + +template +inline T* GetMutableSizePrefixedRootChecked( + void* buf, size_t len, + const Verifier::Options& opts = Verifier::Options()) { + return const_cast(GetSizePrefixedRootChecked( + const_cast(buf), len, opts)); +} + +template +inline T* GetMutableSizePrefixedRootChecked( + void* buf, size_t len, const char* identifier, + const Verifier::Options& opts = Verifier::Options()) { + return const_cast(GetSizePrefixedRootChecked( + const_cast(buf), len, identifier, opts)); +} + /// @brief This return the prefixed size of a FlatBuffer. template inline SizeT GetPrefixedSize(const uint8_t* buf) { diff --git a/tests/monster_test.cpp b/tests/monster_test.cpp index 9b188afa246..8417f97afdf 100644 --- a/tests/monster_test.cpp +++ b/tests/monster_test.cpp @@ -421,6 +421,27 @@ void AccessFlatBufferTest(const uint8_t* flatbuf, size_t length, bool pooled) { TEST_EQ(GetBufferStartFromRootPointer(monster), flatbuf); } +// Basic coverage for the checked root helper APIs that combine verification +// with obtaining the typed root pointer. +void CheckedRootApiTest(const uint8_t* flatbuf, size_t length) { + // Valid buffer should round-trip through the checked helpers. + auto monster_checked = + flatbuffers::GetRootChecked(flatbuf, length, MonsterIdentifier()); + TEST_NOTNULL(monster_checked); + TEST_EQ(monster_checked->hp(), 80); + TEST_EQ(monster_checked->mana(), 150); + + // Mutating the size downward should cause verification to fail and return + // nullptr from the checked helpers, while the unchecked helpers remain + // undefined for such input. + if (length > 4) { + const size_t truncated = length - 4; + auto truncated_checked = + flatbuffers::GetRootChecked(flatbuf, truncated, MonsterIdentifier()); + TEST_EQ(truncated_checked == nullptr, true); + } +} + // Change a FlatBuffer in-place, after it has been constructed. void MutateFlatBuffersTest(uint8_t* flatbuf, std::size_t length) { // Get non-const pointer to root.