diff --git a/include/fc/io/json_relaxed.hpp b/include/fc/io/json_relaxed.hpp index 1ed424b..a9196c2 100644 --- a/include/fc/io/json_relaxed.hpp +++ b/include/fc/io/json_relaxed.hpp @@ -21,7 +21,7 @@ namespace fc { namespace json_relaxed { template - variant variant_from_stream( T& in ); + variant variant_from_stream( T& in, uint32_t depth ); template fc::string tokenFromStream( T& in ) @@ -569,16 +569,18 @@ namespace fc { namespace json_relaxed } FC_CAPTURE_AND_RETHROW( (token) ) } template - variant_object objectFromStream( T& in ) + variant_object objectFromStream( T& in, uint32_t depth ) { - return objectFromStreamBase( in, []( T& in ){ return json_relaxed::stringFromStream( in ); }, - []( T& in ){ return json_relaxed::variant_from_stream( in ); } ); + std::function get_key = []( T& in ){ return json_relaxed::stringFromStream( in ); }; + std::function get_value = [depth]( T& in ){ return json_relaxed::variant_from_stream( in, depth ); }; + return objectFromStreamBase( in, get_key, get_value ); } template - variants arrayFromStream( T& in ) + variants arrayFromStream( T& in, uint32_t depth ) { - return arrayFromStreamBase( in, []( T& in ){ return json_relaxed::variant_from_stream( in ); } ); + std::function get_value = [depth]( T& in ){ return json_relaxed::variant_from_stream( in, depth ); }; + return arrayFromStreamBase( in, get_value ); } template @@ -623,8 +625,9 @@ namespace fc { namespace json_relaxed } template - variant variant_from_stream( T& in ) + variant variant_from_stream( T& in, uint32_t depth ) { + FC_ASSERT( depth < MAX_RECURSION_DEPTH, "Too many nested items in JSON string!" ); skip_white_space(in); signed char c = in.peek(); switch( c ) @@ -632,9 +635,9 @@ namespace fc { namespace json_relaxed case '"': return json_relaxed::stringFromStream( in ); case '{': - return json_relaxed::objectFromStream( in ); + return json_relaxed::objectFromStream( in, depth + 1 ); case '[': - return json_relaxed::arrayFromStream( in ); + return json_relaxed::arrayFromStream( in, depth + 1 ); case '-': case '+': case '.': diff --git a/src/io/json.cpp b/src/io/json.cpp index d3296c9..5d23e70 100644 --- a/src/io/json.cpp +++ b/src/io/json.cpp @@ -14,15 +14,15 @@ namespace fc { // forward declarations of provided functions - template variant variant_from_stream( T& in ); + template variant variant_from_stream( T& in, uint32_t depth = 0 ); template char parseEscape( T& in ); template fc::string stringFromStream( T& in ); template bool skip_white_space( T& in ); template fc::string stringFromToken( T& in ); template variant_object objectFromStreamBase( T& in, std::function& get_key, std::function& get_value ); - template variant_object objectFromStream( T& in ); + template variant_object objectFromStream( T& in, uint32_t depth ); template variants arrayFromStreamBase( T& in, std::function& get_value ); - template variants arrayFromStream( T& in ); + template variants arrayFromStream( T& in, uint32_t depth ); template variant number_from_stream( T& in ); template variant token_from_stream( T& in ); void escape_string( const string& str, ostream& os ); @@ -32,6 +32,8 @@ namespace fc fc::string pretty_print( const fc::string& v, uint8_t indent ); } +#define MAX_RECURSION_DEPTH 200 + #include namespace fc @@ -169,7 +171,7 @@ namespace fc } template - variant_object objectFromStreamBase( T& in, std::string (*get_key)(T&), variant (*get_value)(T&) ) + variant_object objectFromStreamBase( T& in, std::function& get_key, std::function& get_value ) { mutable_variant_object obj; try @@ -218,14 +220,15 @@ namespace fc } template - variant_object objectFromStream( T& in ) + variant_object objectFromStream( T& in, uint32_t depth ) { - return objectFromStreamBase( in, []( T& in ){ return stringFromStream( in ); }, - []( T& in ){ return variant_from_stream( in ); } ); + std::function get_key = []( T& in ){ return stringFromStream( in ); }; + std::function get_value = [depth]( T& in ){ return variant_from_stream( in, depth ); }; + return objectFromStreamBase( in, get_key, get_value ); } template - variants arrayFromStreamBase( T& in, variant (*get_value)(T&) ) + variants arrayFromStreamBase( T& in, std::function& get_value ) { variants ar; try @@ -255,9 +258,10 @@ namespace fc } template - variants arrayFromStream( T& in ) + variants arrayFromStream( T& in, uint32_t depth ) { - return arrayFromStreamBase( in, []( T& in ){ return variant_from_stream( in ); } ); + std::function get_value = [depth]( T& in ){ return variant_from_stream( in, depth ); }; + return arrayFromStreamBase( in, get_value ); } template @@ -401,8 +405,10 @@ namespace fc template - variant variant_from_stream( T& in ) + variant variant_from_stream( T& in, uint32_t depth ) { + if( depth > MAX_RECURSION_DEPTH ) + FC_THROW_EXCEPTION( parse_error_exception, "Too many nested items in JSON input!" ); skip_white_space(in); signed char c = in.peek(); switch( c ) @@ -410,9 +416,9 @@ namespace fc case '"': return stringFromStream( in ); case '{': - return objectFromStream( in ); + return objectFromStream( in, depth + 1 ); case '[': - return arrayFromStream( in ); + return arrayFromStream( in, depth + 1 ); case '-': case '.': case '0': @@ -441,30 +447,8 @@ namespace fc } } - - /** the purpose of this check is to verify that we will not get a stack overflow in the recursive descent parser */ - void check_string_depth( const string& utf8_str ) - { - int32_t open_object = 0; - int32_t open_array = 0; - for( auto c : utf8_str ) - { - switch( c ) - { - case '{': open_object++; break; - case '}': open_object--; break; - case '[': open_array++; break; - case ']': open_array--; break; - default: break; - } - FC_ASSERT( open_object < 100 && open_array < 100, "object graph too deep", ("object depth",open_object)("array depth", open_array) ); - } - } - variant json::from_string( const std::string& utf8_str, parse_type ptype ) { try { - check_string_depth( utf8_str ); - fc::istream_ptr in( new fc::stringstream( utf8_str ) ); fc::buffered_istream bin( in ); return from_stream( bin, ptype ); @@ -474,10 +458,9 @@ namespace fc { variants result; try { - check_string_depth( utf8_str ); fc::stringstream in( utf8_str ); while( true ) - result.push_back(json_relaxed::variant_from_stream( in )); + result.push_back(json_relaxed::variant_from_stream( in, 0 )); } catch ( const fc::eof_exception& ) { return result; } FC_RETHROW_EXCEPTIONS( warn, "", ("str",utf8_str) )