mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-27 15:22:20 +00:00
Protected parser against infinite recursion.
Will error-out after e.g. 64 levels of nested JSON tables. Change-Id: I3ab66cdd509378bfab87b85f85c07ab42aded788 Tested: on Linux.
This commit is contained in:
@@ -525,7 +525,8 @@ class Parser : public ParserState {
|
|||||||
opts(options),
|
opts(options),
|
||||||
uses_flexbuffers_(false),
|
uses_flexbuffers_(false),
|
||||||
source_(nullptr),
|
source_(nullptr),
|
||||||
anonymous_counter(0) {
|
anonymous_counter(0),
|
||||||
|
recurse_protection_counter(0) {
|
||||||
// Start out with the empty namespace being current.
|
// Start out with the empty namespace being current.
|
||||||
empty_namespace_ = new Namespace();
|
empty_namespace_ = new Namespace();
|
||||||
namespaces_.push_back(empty_namespace_);
|
namespaces_.push_back(empty_namespace_);
|
||||||
@@ -704,6 +705,15 @@ class Parser : public ParserState {
|
|||||||
bool SupportsVectorOfUnions() const;
|
bool SupportsVectorOfUnions() const;
|
||||||
Namespace *UniqueNamespace(Namespace *ns);
|
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:
|
public:
|
||||||
SymbolTable<Type> types_;
|
SymbolTable<Type> types_;
|
||||||
SymbolTable<StructDef> structs_;
|
SymbolTable<StructDef> structs_;
|
||||||
@@ -736,6 +746,7 @@ class Parser : public ParserState {
|
|||||||
std::vector<std::pair<Value, FieldDef *>> field_stack_;
|
std::vector<std::pair<Value, FieldDef *>> field_stack_;
|
||||||
|
|
||||||
int anonymous_counter;
|
int anonymous_counter;
|
||||||
|
int recurse_protection_counter;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Utility functions for multiple generators:
|
// Utility functions for multiple generators:
|
||||||
|
|||||||
@@ -113,6 +113,11 @@ CheckedError Parser::Error(const std::string &msg) {
|
|||||||
|
|
||||||
inline CheckedError NoError() { return CheckedError(false); }
|
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,
|
inline std::string OutOfRangeErrorMsg(int64_t val, const std::string &op,
|
||||||
int64_t limit) {
|
int64_t limit) {
|
||||||
const std::string cause = NumToString(val) + op + NumToString(limit);
|
const std::string cause = NumToString(val) + op + NumToString(limit);
|
||||||
@@ -583,7 +588,7 @@ CheckedError Parser::ParseType(Type &type) {
|
|||||||
} else if (token_ == '[') {
|
} else if (token_ == '[') {
|
||||||
NEXT();
|
NEXT();
|
||||||
Type subtype;
|
Type subtype;
|
||||||
ECHECK(ParseType(subtype));
|
ECHECK(Recurse([&]() { return ParseType(subtype); }));
|
||||||
if (subtype.base_type == BASE_TYPE_VECTOR) {
|
if (subtype.base_type == BASE_TYPE_VECTOR) {
|
||||||
// We could support this, but it will complicate things, and it's
|
// We could support this, but it will complicate things, and it's
|
||||||
// easier to work around with a struct around the inner vector.
|
// 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,
|
fieldn_outer, &struct_def,
|
||||||
[](const std::string &name, size_t &fieldn,
|
[](const std::string &name, size_t &fieldn,
|
||||||
const StructDef *struct_def_inner, void *state) -> CheckedError {
|
const StructDef *struct_def_inner, void *state) -> CheckedError {
|
||||||
Parser *parser = static_cast<Parser *>(state);
|
auto *parser = static_cast<Parser *>(state);
|
||||||
if (name == "$schema") {
|
if (name == "$schema") {
|
||||||
ECHECK(parser->Expect(kTokenStringConstant));
|
ECHECK(parser->Expect(kTokenStringConstant));
|
||||||
return NoError();
|
return NoError();
|
||||||
@@ -1006,8 +1011,10 @@ CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
|
|||||||
ECHECK(parser->ParseNestedFlatbuffer(val, field, fieldn,
|
ECHECK(parser->ParseNestedFlatbuffer(val, field, fieldn,
|
||||||
struct_def_inner));
|
struct_def_inner));
|
||||||
} else {
|
} else {
|
||||||
ECHECK(
|
ECHECK(parser->Recurse([&]() {
|
||||||
parser->ParseAnyValue(val, field, fieldn, struct_def_inner));
|
return parser->ParseAnyValue(val, field, fieldn,
|
||||||
|
struct_def_inner);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
// Hardcoded insertion-sort with error-check.
|
// Hardcoded insertion-sort with error-check.
|
||||||
// If fields are specified in order, then this loop exits
|
// 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;
|
auto *parser = parser_and_type->first;
|
||||||
Value val;
|
Value val;
|
||||||
val.type = parser_and_type->second;
|
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));
|
parser->field_stack_.push_back(std::make_pair(val, nullptr));
|
||||||
return NoError();
|
return NoError();
|
||||||
},
|
},
|
||||||
@@ -2130,7 +2139,9 @@ CheckedError Parser::SkipAnyJsonValue() {
|
|||||||
[](const std::string &, size_t &fieldn, const StructDef *,
|
[](const std::string &, size_t &fieldn, const StructDef *,
|
||||||
void *state) -> CheckedError {
|
void *state) -> CheckedError {
|
||||||
auto *parser = static_cast<Parser *>(state);
|
auto *parser = static_cast<Parser *>(state);
|
||||||
ECHECK(parser->SkipAnyJsonValue());
|
ECHECK(parser->Recurse([&]() {
|
||||||
|
return parser->SkipAnyJsonValue();
|
||||||
|
}));
|
||||||
fieldn++;
|
fieldn++;
|
||||||
return NoError();
|
return NoError();
|
||||||
},
|
},
|
||||||
@@ -2141,7 +2152,10 @@ CheckedError Parser::SkipAnyJsonValue() {
|
|||||||
return ParseVectorDelimiters(
|
return ParseVectorDelimiters(
|
||||||
count,
|
count,
|
||||||
[](size_t &, void *state) -> CheckedError {
|
[](size_t &, void *state) -> CheckedError {
|
||||||
return static_cast<Parser *>(state)->SkipAnyJsonValue();
|
auto *parser = static_cast<Parser *>(state);
|
||||||
|
return parser->Recurse([&]() {
|
||||||
|
return parser->SkipAnyJsonValue();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user