Commit a22c0b2a authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'topic/neverlord/json-fix'

parents 846503b3 0904018c
...@@ -18,6 +18,8 @@ is based on [Keep a Changelog](https://keepachangelog.com). ...@@ -18,6 +18,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
- Fix undefined behavior in the Qt group chat example (#1336). - Fix undefined behavior in the Qt group chat example (#1336).
- The `..._instance` convenience functions on the registry metric now properly - The `..._instance` convenience functions on the registry metric now properly
support `double` metrics and histograms. support `double` metrics and histograms.
- Parsing deeply nested JSON inputs no longer produces a stack overflow.
Instead, the parser rejects any JSON with too many nesting levels.
### Changed ### Changed
......
...@@ -66,6 +66,8 @@ enum class pec : uint8_t { ...@@ -66,6 +66,8 @@ enum class pec : uint8_t {
/// Stopped after running into an invalid parser state. Should never happen /// Stopped after running into an invalid parser state. Should never happen
/// and most likely indicates a bug in the implementation. /// and most likely indicates a bug in the implementation.
invalid_state, invalid_state,
/// Parser stopped after exceeding its maximum supported level of nesting.
nested_too_deeply,
}; };
/// @relates pec /// @relates pec
......
...@@ -19,6 +19,12 @@ CAF_PUSH_UNUSED_LABEL_WARNING ...@@ -19,6 +19,12 @@ CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp" #include "caf/detail/parser/fsm.hpp"
namespace {
constexpr size_t max_nesting_level = 128;
} // namespace
namespace caf::detail::parser { namespace caf::detail::parser {
struct obj_consumer; struct obj_consumer;
...@@ -93,7 +99,8 @@ obj_consumer val_consumer::begin_object() { ...@@ -93,7 +99,8 @@ obj_consumer val_consumer::begin_object() {
return {&obj}; return {&obj};
} }
void read_value(string_parser_state& ps, val_consumer consumer); void read_value(string_parser_state& ps, size_t nesting_level,
val_consumer consumer);
template <class Consumer> template <class Consumer>
void read_json_null_or_nan(string_parser_state& ps, Consumer consumer) { void read_json_null_or_nan(string_parser_state& ps, Consumer consumer) {
...@@ -161,7 +168,8 @@ void read_json_string(string_parser_state& ps, Consumer consumer) { ...@@ -161,7 +168,8 @@ void read_json_string(string_parser_state& ps, Consumer consumer) {
// clang-format on // clang-format on
} }
void read_member(string_parser_state& ps, member_consumer consumer) { void read_member(string_parser_state& ps, size_t nesting_level,
member_consumer consumer) {
// clang-format off // clang-format off
start(); start();
state(init) { state(init) {
...@@ -170,7 +178,8 @@ void read_member(string_parser_state& ps, member_consumer consumer) { ...@@ -170,7 +178,8 @@ void read_member(string_parser_state& ps, member_consumer consumer) {
} }
state(after_key) { state(after_key) {
transition(after_key, " \t\n") transition(after_key, " \t\n")
fsm_transition(read_value(ps, consumer.begin_val()), done, ':') fsm_transition(read_value(ps, nesting_level, consumer.begin_val()),
done, ':')
} }
term_state(done) { term_state(done) {
transition(done, " \t\n") transition(done, " \t\n")
...@@ -179,7 +188,12 @@ void read_member(string_parser_state& ps, member_consumer consumer) { ...@@ -179,7 +188,12 @@ void read_member(string_parser_state& ps, member_consumer consumer) {
// clang-format on // clang-format on
} }
void read_json_object(string_parser_state& ps, obj_consumer consumer) { void read_json_object(string_parser_state& ps, size_t nesting_level,
obj_consumer consumer) {
if (nesting_level >= max_nesting_level) {
ps.code = pec::nested_too_deeply;
return;
}
// clang-format off // clang-format off
start(); start();
state(init) { state(init) {
...@@ -188,7 +202,8 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) { ...@@ -188,7 +202,8 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) {
} }
state(has_open_brace) { state(has_open_brace) {
transition(has_open_brace, " \t\n") transition(has_open_brace, " \t\n")
fsm_epsilon(read_member(ps, consumer.begin_member()), after_member, '"') fsm_epsilon(read_member(ps, nesting_level + 1, consumer.begin_member()),
after_member, '"')
transition(done, '}') transition(done, '}')
} }
state(after_member) { state(after_member) {
...@@ -198,7 +213,8 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) { ...@@ -198,7 +213,8 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) {
} }
state(after_comma) { state(after_comma) {
transition(after_comma, " \t\n") transition(after_comma, " \t\n")
fsm_epsilon(read_member(ps, consumer.begin_member()), after_member, '"') fsm_epsilon(read_member(ps, nesting_level + 1, consumer.begin_member()),
after_member, '"')
} }
term_state(done) { term_state(done) {
transition(done, " \t\n") transition(done, " \t\n")
...@@ -207,7 +223,12 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) { ...@@ -207,7 +223,12 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) {
// clang-format on // clang-format on
} }
void read_json_array(string_parser_state& ps, arr_consumer consumer) { void read_json_array(string_parser_state& ps, size_t nesting_level,
arr_consumer consumer) {
if (nesting_level >= max_nesting_level) {
ps.code = pec::nested_too_deeply;
return;
}
// clang-format off // clang-format off
start(); start();
state(init) { state(init) {
...@@ -217,7 +238,8 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) { ...@@ -217,7 +238,8 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) {
state(has_open_brace) { state(has_open_brace) {
transition(has_open_brace, " \t\n") transition(has_open_brace, " \t\n")
transition(done, ']') transition(done, ']')
fsm_epsilon(read_value(ps, consumer.begin_value()), after_value) fsm_epsilon(read_value(ps, nesting_level + 1, consumer.begin_value()),
after_value)
} }
state(after_value) { state(after_value) {
transition(after_value, " \t\n") transition(after_value, " \t\n")
...@@ -226,7 +248,8 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) { ...@@ -226,7 +248,8 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) {
} }
state(after_comma) { state(after_comma) {
transition(after_comma, " \t\n") transition(after_comma, " \t\n")
fsm_epsilon(read_value(ps, consumer.begin_value()), after_value) fsm_epsilon(read_value(ps, nesting_level + 1, consumer.begin_value()),
after_value)
} }
term_state(done) { term_state(done) {
transition(done, " \t\n") transition(done, " \t\n")
...@@ -235,7 +258,8 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) { ...@@ -235,7 +258,8 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) {
// clang-format on // clang-format on
} }
void read_value(string_parser_state& ps, val_consumer consumer) { void read_value(string_parser_state& ps, size_t nesting_level,
val_consumer consumer) {
// clang-format off // clang-format off
start(); start();
state(init) { state(init) {
...@@ -244,8 +268,10 @@ void read_value(string_parser_state& ps, val_consumer consumer) { ...@@ -244,8 +268,10 @@ void read_value(string_parser_state& ps, val_consumer consumer) {
fsm_epsilon(read_bool(ps, consumer), done, "ft") fsm_epsilon(read_bool(ps, consumer), done, "ft")
fsm_epsilon(read_json_null_or_nan(ps, consumer), done, "n") fsm_epsilon(read_json_null_or_nan(ps, consumer), done, "n")
fsm_epsilon(read_number(ps, consumer), done, "+-.0123456789") fsm_epsilon(read_number(ps, consumer), done, "+-.0123456789")
fsm_epsilon(read_json_object(ps, consumer.begin_object()), done, '{') fsm_epsilon(read_json_object(ps, nesting_level, consumer.begin_object()),
fsm_epsilon(read_json_array(ps, consumer.begin_array()), done, '[') done, '{')
fsm_epsilon(read_json_array(ps, nesting_level, consumer.begin_array()),
done, '[')
} }
term_state(done) { term_state(done) {
transition(done, " \t\n") transition(done, " \t\n")
...@@ -300,7 +326,7 @@ object* make_object(monotonic_buffer_resource* storage) { ...@@ -300,7 +326,7 @@ object* make_object(monotonic_buffer_resource* storage) {
value* parse(string_parser_state& ps, monotonic_buffer_resource* storage) { value* parse(string_parser_state& ps, monotonic_buffer_resource* storage) {
monotonic_buffer_resource::allocator<value> alloc{storage}; monotonic_buffer_resource::allocator<value> alloc{storage};
auto result = new (alloc.allocate(1)) value(); auto result = new (alloc.allocate(1)) value();
parser::read_value(ps, {storage, result}); parser::read_value(ps, 0, {storage, result});
return result; return result;
} }
......
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