Skip to content

Commit ea0a73d

Browse files
authored
disallow circular struct references (#8851)
* detect and fail for circular struct dependencies * pr comments * pr comment
1 parent 4623cfa commit ea0a73d

File tree

4 files changed

+60
-1
lines changed

4 files changed

+60
-1
lines changed

include/flatbuffers/idl.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,13 +395,20 @@ struct FieldDef : public Definition {
395395
};
396396

397397
struct StructDef : public Definition {
398+
enum class CycleStatus {
399+
NotChecked,
400+
InProgress,
401+
Checked,
402+
};
403+
398404
StructDef()
399405
: fixed(false),
400406
predecl(true),
401407
sortbysize(true),
402408
has_key(false),
403409
minalign(1),
404-
bytesize(0) {}
410+
bytesize(0),
411+
cycle_status{CycleStatus::NotChecked} {}
405412

406413
void PadLastField(size_t min_align) {
407414
auto padding = PaddingBytes(bytesize, min_align);
@@ -423,6 +430,8 @@ struct StructDef : public Definition {
423430
size_t minalign; // What the whole object needs to be aligned to.
424431
size_t bytesize; // Size if fixed.
425432

433+
CycleStatus cycle_status; // used for determining if we have circular references
434+
426435
flatbuffers::unique_ptr<std::string> original_location;
427436
std::vector<voffset_t> reserved_ids;
428437
};
@@ -1101,6 +1110,8 @@ class Parser : public ParserState {
11011110
// others includes.
11021111
std::vector<IncludedFile> GetIncludedFiles() const;
11031112

1113+
bool HasCircularStructDependency();
1114+
11041115
private:
11051116
class ParseDepthGuard;
11061117

src/flatc.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,9 @@ std::unique_ptr<Parser> FlatCompiler::GenerateCode(const FlatCOptions& options,
927927
auto err = parser->ConformTo(conform_parser);
928928
if (!err.empty()) Error("schemas don\'t conform: " + err, false);
929929
}
930+
if (parser->HasCircularStructDependency()) {
931+
Error("schema has circular struct dependencies: " + filename, false);
932+
}
930933
if (options.schema_binary || opts.binary_schema_gen_embed) {
931934
parser->Serialize();
932935
}

src/idl_parser.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2755,6 +2755,42 @@ std::vector<IncludedFile> Parser::GetIncludedFiles() const {
27552755
return {it->second.cbegin(), it->second.cend()};
27562756
}
27572757

2758+
bool Parser::HasCircularStructDependency() {
2759+
std::function<bool(StructDef*)> visit =
2760+
[&](StructDef* struct_def) {
2761+
// Only consider fixed structs and structs we have yet to check
2762+
if (!struct_def->fixed || struct_def->cycle_status == StructDef::CycleStatus::Checked) {
2763+
return false;
2764+
}
2765+
2766+
if (struct_def->cycle_status == StructDef::CycleStatus::InProgress) {
2767+
// cycle found
2768+
return true;
2769+
}
2770+
2771+
struct_def->cycle_status = StructDef::CycleStatus::InProgress;
2772+
2773+
for (const auto& field : struct_def->fields.vec) {
2774+
if (field->value.type.base_type == BASE_TYPE_STRUCT) {
2775+
if (visit(field->value.type.struct_def)) {
2776+
return true; // Cycle detected in recursion.
2777+
}
2778+
}
2779+
}
2780+
2781+
struct_def->cycle_status = StructDef::CycleStatus::Checked;
2782+
return false; // No cycle detected.
2783+
};
2784+
2785+
for (const auto& struct_def : structs_.vec) {
2786+
if (visit(struct_def)) {
2787+
return true; // Cycle detected.
2788+
}
2789+
}
2790+
2791+
return false; // No cycle detected.
2792+
}
2793+
27582794
bool Parser::SupportsOptionalScalars(const flatbuffers::IDLOptions& opts) {
27592795
static FLATBUFFERS_CONSTEXPR unsigned long supported_langs =
27602796
IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kLobster |

tests/flatc/flatc_schema_tests.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,12 @@ def EnumValAttributes(self):
7171
schema_json["enums"][0]["values"][2]["attributes"][1]["value"]
7272
== "Value 3 (deprecated)"
7373
)
74+
75+
def CircularStructDependency(self):
76+
try:
77+
flatc(["-c", "circular_struct_dependency.fbs"])
78+
assert False, "Expected flatc to fail on circular struct dependency"
79+
except subprocess.CalledProcessError:
80+
pass
81+
82+
flatc(["-c", "circular_table.fbs"])

0 commit comments

Comments
 (0)