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
79 changes: 79 additions & 0 deletions include/flatbuffers/flatbuffers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename T>
inline const T* GetRootChecked(
const void* buf, size_t len,
const Verifier::Options& opts = Verifier::Options()) {
if (!buf) return nullptr;
Verifier verifier(reinterpret_cast<const uint8_t*>(buf), len, opts);
if (!verifier.VerifyBuffer<T>()) return nullptr;
return GetRoot<T>(buf);
}

template <typename T>
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<const uint8_t*>(buf), len, opts);
if (!verifier.VerifyBuffer<T>(identifier)) return nullptr;
return GetRoot<T>(buf);
}

template <typename T, typename SizeT = uoffset_t>
inline const T* GetSizePrefixedRootChecked(
const void* buf, size_t len,
const Verifier::Options& opts = Verifier::Options()) {
if (!buf) return nullptr;
Verifier verifier(reinterpret_cast<const uint8_t*>(buf), len, opts);
if (!verifier.VerifySizePrefixedBuffer<T, SizeT>(nullptr)) return nullptr;
return GetSizePrefixedRoot<T, SizeT>(buf);
}

template <typename T, typename SizeT = uoffset_t>
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<const uint8_t*>(buf), len, opts);
if (!verifier.VerifySizePrefixedBuffer<T, SizeT>(identifier)) return nullptr;
return GetSizePrefixedRoot<T, SizeT>(buf);
}

template <typename T>
inline T* GetMutableRootChecked(
void* buf, size_t len,
const Verifier::Options& opts = Verifier::Options()) {
return const_cast<T*>(
GetRootChecked<T>(const_cast<const void*>(buf), len, opts));
}

template <typename T>
inline T* GetMutableRootChecked(
void* buf, size_t len, const char* identifier,
const Verifier::Options& opts = Verifier::Options()) {
return const_cast<T*>(GetRootChecked<T>(const_cast<const void*>(buf), len,
identifier, opts));
}

template <typename T, typename SizeT = uoffset_t>
inline T* GetMutableSizePrefixedRootChecked(
void* buf, size_t len,
const Verifier::Options& opts = Verifier::Options()) {
return const_cast<T*>(GetSizePrefixedRootChecked<T, SizeT>(
const_cast<const void*>(buf), len, opts));
}

template <typename T, typename SizeT = uoffset_t>
inline T* GetMutableSizePrefixedRootChecked(
void* buf, size_t len, const char* identifier,
const Verifier::Options& opts = Verifier::Options()) {
return const_cast<T*>(GetSizePrefixedRootChecked<T, SizeT>(
const_cast<const void*>(buf), len, identifier, opts));
}

/// @brief This return the prefixed size of a FlatBuffer.
template <typename SizeT = uoffset_t>
inline SizeT GetPrefixedSize(const uint8_t* buf) {
Expand Down
21 changes: 21 additions & 0 deletions tests/monster_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Monster>(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<Monster>(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.
Expand Down