Protected parser against infinite recursion.

Will error-out after e.g. 64 levels of nested JSON tables.

Change-Id: I3ab66cdd509378bfab87b85f85c07ab42aded788
Tested: on Linux.
parent f9c64891
......@@ -525,7 +525,8 @@ class Parser : public ParserState {
opts(options),
uses_flexbuffers_(false),
source_(nullptr),
anonymous_counter(0) {
anonymous_counter(0),
recurse_protection_counter(0) {
// Start out with the empty namespace being current.
empty_namespace_ = new Namespace();
namespaces_.push_back(empty_namespace_);
......@@ -704,6 +705,15 @@ class Parser : public ParserState {
bool SupportsVectorOfUnions() const;
Namespace *UniqueNamespace(Namespace *ns);
enum { kMaxParsingDepth = 64 };
FLATBUFFERS_CHECKED_ERROR RecurseError();
template<typename F> CheckedError Recurse(F f) {
if (++recurse_protection_counter >= kMaxParsingDepth) return RecurseError();
auto ce = f();
recurse_protection_counter--;
return ce;
}
public:
SymbolTable<Type> types_;
SymbolTable<StructDef> structs_;
......@@ -736,6 +746,7 @@ class Parser : public ParserState {
std::vector<std::pair<Value, FieldDef *>> field_stack_;
int anonymous_counter;
int recurse_protection_counter;
};
// Utility functions for multiple generators:
......
......@@ -113,6 +113,11 @@ CheckedError Parser::Error(const std::string &msg) {
inline CheckedError NoError() { return CheckedError(false); }
CheckedError Parser::RecurseError() {
return Error("maximum parsing recursion of " + NumToString(kMaxParsingDepth) +
" reached");
}
inline std::string OutOfRangeErrorMsg(int64_t val, const std::string &op,
int64_t limit) {
const std::string cause = NumToString(val) + op + NumToString(limit);
......@@ -583,7 +588,7 @@ CheckedError Parser::ParseType(Type &type) {
} else if (token_ == '[') {
NEXT();
Type subtype;
ECHECK(ParseType(subtype));
ECHECK(Recurse([&]() { return ParseType(subtype); }));
if (subtype.base_type == BASE_TYPE_VECTOR) {
// We could support this, but it will complicate things, and it's
// easier to work around with a struct around the inner vector.
......@@ -975,7 +980,7 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
fieldn_outer, &struct_def,
[](const std::string &name, size_t &fieldn,
const StructDef *struct_def_inner, void *state) -> CheckedError {
Parser *parser = static_cast<Parser *>(state);
auto *parser = static_cast<Parser *>(state);
if (name == "$schema") {
ECHECK(parser->Expect(kTokenStringConstant));
return NoError();
......@@ -1006,8 +1011,10 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
ECHECK(parser->ParseNestedFlatbuffer(val, field, fieldn,
struct_def_inner));
} else {
ECHECK(
parser->ParseAnyValue(val, field, fieldn, struct_def_inner));
ECHECK(parser->Recurse([&]() {
return parser->ParseAnyValue(val, field, fieldn,
struct_def_inner);
}));
}
// Hardcoded insertion-sort with error-check.
// If fields are specified in order, then this loop exits
......@@ -1152,7 +1159,9 @@ CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue) {
auto *parser = parser_and_type->first;
Value val;
val.type = parser_and_type->second;
ECHECK(parser->ParseAnyValue(val, nullptr, 0, nullptr));
ECHECK(parser->Recurse([&]() {
return parser->ParseAnyValue(val, nullptr, 0, nullptr);
}));
parser->field_stack_.push_back(std::make_pair(val, nullptr));
return NoError();
},
......@@ -2130,7 +2139,9 @@ CheckedError Parser::SkipAnyJsonValue() {
[](const std::string &, size_t &fieldn, const StructDef *,
void *state) -> CheckedError {
auto *parser = static_cast<Parser *>(state);
ECHECK(parser->SkipAnyJsonValue());
ECHECK(parser->Recurse([&]() {
return parser->SkipAnyJsonValue();
}));
fieldn++;
return NoError();
},
......@@ -2141,7 +2152,10 @@ CheckedError Parser::SkipAnyJsonValue() {
return ParseVectorDelimiters(
count,
[](size_t &, void *state) -> CheckedError {
return static_cast<Parser *>(state)->SkipAnyJsonValue();
auto *parser = static_cast<Parser *>(state);
return parser->Recurse([&]() {
return parser->SkipAnyJsonValue();
});
},
this);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment