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
47 changes: 38 additions & 9 deletions src/binary_annotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -753,8 +753,16 @@ void BinaryAnnotator::BuildTable(const uint64_t table_offset,

switch (field->type()->base_type()) {
case reflection::BaseType::Obj: {
const reflection::Object* next_object =
schema_->objects()->Get(field->type()->index());
const reflection::Object* next_object = GetObject(field);
if (next_object == nullptr) {
// Field references a non-existent Object in the schema. Treat as a
// generic offset field annotation.
offset_field_comment.default_value = "(unknown)";
regions.push_back(MakeBinaryRegion(field_offset, length, region_type,
0, offset_of_next_item,
offset_field_comment));
break;
}

if (next_object->is_struct()) {
// Structs are stored inline.
Expand Down Expand Up @@ -911,9 +919,14 @@ uint64_t BinaryAnnotator::BuildStruct(const uint64_t struct_offset,
offset += type_size;
} else if (field->type()->base_type() == reflection::BaseType::Obj) {
// Structs are stored inline, even when nested.
const reflection::Object* nested = GetObject(field);
if (nested == nullptr) {
// OOB Object index; skip further traversal.
return;
}
offset = BuildStruct(offset, regions,
referring_field_name + "." + field->name()->str(),
schema_->objects()->Get(field->type()->index()));
nested);
} else if (field->type()->base_type() == reflection::BaseType::Array) {
const bool is_scalar = IsScalar(field->type()->element());
const uint64_t type_size = GetTypeSize(field->type()->element());
Expand Down Expand Up @@ -961,10 +974,15 @@ uint64_t BinaryAnnotator::BuildStruct(const uint64_t struct_offset,
// TODO(dbaileychess): This works, but the comments on the fields lose
// some context. Need to figure a way how to plumb the nested arrays
// comments together that isn't too confusing.
const reflection::Object* nested = GetObject(field);
if (nested == nullptr) {
// OOB Object index; skip further traversal.
break;
}
offset =
BuildStruct(offset, regions,
referring_field_name + "." + field->name()->str(),
schema_->objects()->Get(field->type()->index()));
nested);
}
}
}
Expand Down Expand Up @@ -1131,8 +1149,11 @@ void BinaryAnnotator::BuildVector(

switch (field->type()->element()) {
case reflection::BaseType::Obj: {
const reflection::Object* object =
schema_->objects()->Get(field->type()->index());
const reflection::Object* object = GetObject(field);
if (object == nullptr) {
// OOB Object index; cannot annotate further.
return;
}

if (object->is_struct()) {
// Vector of structs
Expand Down Expand Up @@ -1408,8 +1429,11 @@ void BinaryAnnotator::BuildVector(
std::string BinaryAnnotator::BuildUnion(const uint64_t union_offset,
const uint8_t realized_type,
const reflection::Field* const field) {
const reflection::Enum* next_enum =
schema_->enums()->Get(field->type()->index());
const reflection::Enum* next_enum = GetEnum(field);
if (next_enum == nullptr) {
// OOB Enum index; cannot annotate further.
return "";
}

const reflection::EnumVal* enum_val = next_enum->values()->Get(realized_type);

Expand All @@ -1420,8 +1444,13 @@ std::string BinaryAnnotator::BuildUnion(const uint64_t union_offset,
const reflection::Type* union_type = enum_val->union_type();

if (union_type->base_type() == reflection::BaseType::Obj) {
const int32_t union_index = union_type->index();
if (union_index < 0 ||
static_cast<uint32_t>(union_index) >= schema_->objects()->size()) {
return enum_val->name()->c_str();
}
const reflection::Object* object =
schema_->objects()->Get(union_type->index());
schema_->objects()->Get(static_cast<uint32_t>(union_index));

if (object->is_struct()) {
// Union of vectors point to a new Binary section
Expand Down
30 changes: 28 additions & 2 deletions src/binary_annotator.h
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,9 @@ class BinaryAnnotator {

bool IsInlineField(const reflection::Field* const field) {
if (field->type()->base_type() == reflection::BaseType::Obj) {
return schema_->objects()->Get(field->type()->index())->is_struct();
const reflection::Object* object = GetObject(field);
if (object == nullptr) return false;
return object->is_struct();
}
return IsScalar(field->type()->base_type());
}
Expand Down Expand Up @@ -426,14 +428,38 @@ class BinaryAnnotator {
return value < enum_def->values()->size();
}

// Returns the Object referenced by `field`, or nullptr if the index is out of
// bounds. The .bfbs file is user-controlled; without this check the
// BinaryAnnotator dereferences out-of-bounds memory.
const reflection::Object* GetObject(const reflection::Field* const field) {
const int32_t index = field->type()->index();
if (index < 0 ||
static_cast<uint32_t>(index) >= schema_->objects()->size()) {
return nullptr;
}
return schema_->objects()->Get(static_cast<uint32_t>(index));
}

// Returns the Enum referenced by `field`, or nullptr if the index is out of
// bounds.
const reflection::Enum* GetEnum(const reflection::Field* const field) {
const int32_t index = field->type()->index();
if (index < 0 ||
static_cast<uint32_t>(index) >= schema_->enums()->size()) {
return nullptr;
}
return schema_->enums()->Get(static_cast<uint32_t>(index));
}

uint64_t GetElementSize(const reflection::Field* const field) {
if (IsScalar(field->type()->element())) {
return GetTypeSize(field->type()->element());
}

switch (field->type()->element()) {
case reflection::BaseType::Obj: {
auto obj = schema_->objects()->Get(field->type()->index());
const reflection::Object* obj = GetObject(field);
if (obj == nullptr) return sizeof(uint32_t);
return obj->is_struct() ? obj->bytesize() : sizeof(uint32_t);
}
default:
Expand Down