forked from BigfootDev/flatbuffers
disallow circular struct references (#8851)
* detect and fail for circular struct dependencies * pr comments * pr comment
This commit is contained in:
@@ -395,13 +395,20 @@ struct FieldDef : public Definition {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct StructDef : public Definition {
|
struct StructDef : public Definition {
|
||||||
|
enum class CycleStatus {
|
||||||
|
NotChecked,
|
||||||
|
InProgress,
|
||||||
|
Checked,
|
||||||
|
};
|
||||||
|
|
||||||
StructDef()
|
StructDef()
|
||||||
: fixed(false),
|
: fixed(false),
|
||||||
predecl(true),
|
predecl(true),
|
||||||
sortbysize(true),
|
sortbysize(true),
|
||||||
has_key(false),
|
has_key(false),
|
||||||
minalign(1),
|
minalign(1),
|
||||||
bytesize(0) {}
|
bytesize(0),
|
||||||
|
cycle_status{CycleStatus::NotChecked} {}
|
||||||
|
|
||||||
void PadLastField(size_t min_align) {
|
void PadLastField(size_t min_align) {
|
||||||
auto padding = PaddingBytes(bytesize, min_align);
|
auto padding = PaddingBytes(bytesize, min_align);
|
||||||
@@ -423,6 +430,8 @@ struct StructDef : public Definition {
|
|||||||
size_t minalign; // What the whole object needs to be aligned to.
|
size_t minalign; // What the whole object needs to be aligned to.
|
||||||
size_t bytesize; // Size if fixed.
|
size_t bytesize; // Size if fixed.
|
||||||
|
|
||||||
|
CycleStatus cycle_status; // used for determining if we have circular references
|
||||||
|
|
||||||
flatbuffers::unique_ptr<std::string> original_location;
|
flatbuffers::unique_ptr<std::string> original_location;
|
||||||
std::vector<voffset_t> reserved_ids;
|
std::vector<voffset_t> reserved_ids;
|
||||||
};
|
};
|
||||||
@@ -1101,6 +1110,8 @@ class Parser : public ParserState {
|
|||||||
// others includes.
|
// others includes.
|
||||||
std::vector<IncludedFile> GetIncludedFiles() const;
|
std::vector<IncludedFile> GetIncludedFiles() const;
|
||||||
|
|
||||||
|
bool HasCircularStructDependency();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class ParseDepthGuard;
|
class ParseDepthGuard;
|
||||||
|
|
||||||
|
|||||||
@@ -927,6 +927,9 @@ std::unique_ptr<Parser> FlatCompiler::GenerateCode(const FlatCOptions& options,
|
|||||||
auto err = parser->ConformTo(conform_parser);
|
auto err = parser->ConformTo(conform_parser);
|
||||||
if (!err.empty()) Error("schemas don\'t conform: " + err, false);
|
if (!err.empty()) Error("schemas don\'t conform: " + err, false);
|
||||||
}
|
}
|
||||||
|
if (parser->HasCircularStructDependency()) {
|
||||||
|
Error("schema has circular struct dependencies: " + filename, false);
|
||||||
|
}
|
||||||
if (options.schema_binary || opts.binary_schema_gen_embed) {
|
if (options.schema_binary || opts.binary_schema_gen_embed) {
|
||||||
parser->Serialize();
|
parser->Serialize();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2755,6 +2755,42 @@ std::vector<IncludedFile> Parser::GetIncludedFiles() const {
|
|||||||
return {it->second.cbegin(), it->second.cend()};
|
return {it->second.cbegin(), it->second.cend()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Parser::HasCircularStructDependency() {
|
||||||
|
std::function<bool(StructDef*)> visit =
|
||||||
|
[&](StructDef* struct_def) {
|
||||||
|
// Only consider fixed structs and structs we have yet to check
|
||||||
|
if (!struct_def->fixed || struct_def->cycle_status == StructDef::CycleStatus::Checked) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (struct_def->cycle_status == StructDef::CycleStatus::InProgress) {
|
||||||
|
// cycle found
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct_def->cycle_status = StructDef::CycleStatus::InProgress;
|
||||||
|
|
||||||
|
for (const auto& field : struct_def->fields.vec) {
|
||||||
|
if (field->value.type.base_type == BASE_TYPE_STRUCT) {
|
||||||
|
if (visit(field->value.type.struct_def)) {
|
||||||
|
return true; // Cycle detected in recursion.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct_def->cycle_status = StructDef::CycleStatus::Checked;
|
||||||
|
return false; // No cycle detected.
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& struct_def : structs_.vec) {
|
||||||
|
if (visit(struct_def)) {
|
||||||
|
return true; // Cycle detected.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // No cycle detected.
|
||||||
|
}
|
||||||
|
|
||||||
bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions& opts) {
|
bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions& opts) {
|
||||||
static FLATBUFFERS_CONSTEXPR unsigned long supported_langs =
|
static FLATBUFFERS_CONSTEXPR unsigned long supported_langs =
|
||||||
IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kLobster |
|
IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kLobster |
|
||||||
|
|||||||
@@ -71,3 +71,12 @@ class SchemaTests:
|
|||||||
schema_json["enums"][0]["values"][2]["attributes"][1]["value"]
|
schema_json["enums"][0]["values"][2]["attributes"][1]["value"]
|
||||||
== "Value 3 (deprecated)"
|
== "Value 3 (deprecated)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def CircularStructDependency(self):
|
||||||
|
try:
|
||||||
|
flatc(["-c", "circular_struct_dependency.fbs"])
|
||||||
|
assert False, "Expected flatc to fail on circular struct dependency"
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
flatc(["-c", "circular_table.fbs"])
|
||||||
Reference in New Issue
Block a user