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).
- Fix undefined behavior in the Qt group chat example (#1336).
- The `..._instance` convenience functions on the registry metric now properly
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
......
......@@ -66,6 +66,8 @@ enum class pec : uint8_t {
/// Stopped after running into an invalid parser state. Should never happen
/// and most likely indicates a bug in the implementation.
invalid_state,
/// Parser stopped after exceeding its maximum supported level of nesting.
nested_too_deeply,
};
/// @relates pec
......
......@@ -19,6 +19,12 @@ CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp"
namespace {
constexpr size_t max_nesting_level = 128;
} // namespace
namespace caf::detail::parser {
struct obj_consumer;
......@@ -93,7 +99,8 @@ obj_consumer val_consumer::begin_object() {
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>
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) {
// 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
start();
state(init) {
......@@ -170,7 +178,8 @@ void read_member(string_parser_state& ps, member_consumer consumer) {
}
state(after_key) {
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) {
transition(done, " \t\n")
......@@ -179,7 +188,12 @@ void read_member(string_parser_state& ps, member_consumer consumer) {
// 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
start();
state(init) {
......@@ -188,7 +202,8 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) {
}
state(has_open_brace) {
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, '}')
}
state(after_member) {
......@@ -198,7 +213,8 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) {
}
state(after_comma) {
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) {
transition(done, " \t\n")
......@@ -207,7 +223,12 @@ void read_json_object(string_parser_state& ps, obj_consumer consumer) {
// 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
start();
state(init) {
......@@ -217,7 +238,8 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) {
state(has_open_brace) {
transition(has_open_brace, " \t\n")
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) {
transition(after_value, " \t\n")
......@@ -226,7 +248,8 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) {
}
state(after_comma) {
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) {
transition(done, " \t\n")
......@@ -235,7 +258,8 @@ void read_json_array(string_parser_state& ps, arr_consumer consumer) {
// 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
start();
state(init) {
......@@ -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_json_null_or_nan(ps, consumer), done, "n")
fsm_epsilon(read_number(ps, consumer), done, "+-.0123456789")
fsm_epsilon(read_json_object(ps, consumer.begin_object()), done, '{')
fsm_epsilon(read_json_array(ps, consumer.begin_array()), done, '[')
fsm_epsilon(read_json_object(ps, nesting_level, consumer.begin_object()),
done, '{')
fsm_epsilon(read_json_array(ps, nesting_level, consumer.begin_array()),
done, '[')
}
term_state(done) {
transition(done, " \t\n")
......@@ -300,7 +326,7 @@ object* make_object(monotonic_buffer_resource* storage) {
value* parse(string_parser_state& ps, monotonic_buffer_resource* storage) {
monotonic_buffer_resource::allocator<value> alloc{storage};
auto result = new (alloc.allocate(1)) value();
parser::read_value(ps, {storage, result});
parser::read_value(ps, 0, {storage, 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