diff --git a/include/fc/config.hpp b/include/fc/config.hpp index d08eaf16f..a67a22d6e 100644 --- a/include/fc/config.hpp +++ b/include/fc/config.hpp @@ -6,4 +6,9 @@ #ifndef FC_MAX_LOG_OBJECT_DEPTH // how many levels of nested objects are displayed in log messages #define FC_MAX_LOG_OBJECT_DEPTH 200 -#endif \ No newline at end of file +#endif + +#ifndef FC_MAX_PREALLOC_SIZE +// how many elements will be reserve()d when deserializing vectors +#define FC_MAX_PREALLOC_SIZE (256UL) +#endif diff --git a/include/fc/container/flat.hpp b/include/fc/container/flat.hpp index fac5869b7..5cac53e69 100644 --- a/include/fc/container/flat.hpp +++ b/include/fc/container/flat.hpp @@ -26,7 +26,7 @@ namespace fc { unsigned_int size; unpack( s, size, _max_depth ); value.clear(); FC_ASSERT( size.value*sizeof(T) < MAX_ARRAY_ALLOC_SIZE ); - value.reserve(size.value); + value.reserve( std::min( size.value, FC_MAX_PREALLOC_SIZE ) ); for( uint32_t i = 0; i < size.value; ++i ) { T tmp; @@ -54,7 +54,7 @@ namespace fc { unsigned_int size; unpack( s, size, _max_depth ); value.clear(); FC_ASSERT( size.value*(sizeof(K)+sizeof(V)) < MAX_ARRAY_ALLOC_SIZE ); - value.reserve(size.value); + value.reserve( std::min( size.value, FC_MAX_PREALLOC_SIZE ) ); for( uint32_t i = 0; i < size.value; ++i ) { std::pair tmp; @@ -86,11 +86,16 @@ namespace fc { --_max_depth; unsigned_int size; unpack( s, size, _max_depth ); - value.resize( size ); if( !std::is_fundamental::value ) { - for( auto& item : value ) - unpack( s, item, _max_depth ); + value.resize( std::min( size.value, FC_MAX_PREALLOC_SIZE ) ); + for( uint64_t i = 0; i < size; i++ ) + { + if( i >= value.size() ) + value.resize( std::min( 2*value.size(), size.value ) ); + unpack( s, value[i], _max_depth ); + } } else { + value.resize( size ); s.read( (char*)value.data(), value.size() ); } } diff --git a/include/fc/interprocess/container.hpp b/include/fc/interprocess/container.hpp index a00346e75..a1a46587a 100644 --- a/include/fc/interprocess/container.hpp +++ b/include/fc/interprocess/container.hpp @@ -105,31 +105,4 @@ namespace fc { FC_ASSERT( r == d.size() ); } } - - namespace raw { - namespace bip = boost::interprocess; - - template - inline void pack( Stream& s, const bip::vector& value, uint32_t _max_depth=FC_PACK_MAX_DEPTH ) { - FC_ASSERT( _max_depth > 0 ); - --_max_depth; - pack( s, unsigned_int(value.size()), _max_depth ); - auto itr = value.begin(); - auto end = value.end(); - while( itr != end ) { - fc::raw::pack( s, *itr, _max_depth ); - ++itr; - } - } - template - inline void unpack( Stream& s, bip::vector& value, uint32_t _max_depth=FC_PACK_MAX_DEPTH ) { - FC_ASSERT( _max_depth > 0 ); - --_max_depth; - unsigned_int size; - unpack( s, size, _max_depth ); - value.clear(); value.resize(size); - for( auto& item : value ) - fc::raw::unpack( s, item, _max_depth ); - } - } } diff --git a/include/fc/io/raw.hpp b/include/fc/io/raw.hpp index c12abb1b5..a2ecbc873 100644 --- a/include/fc/io/raw.hpp +++ b/include/fc/io/raw.hpp @@ -429,7 +429,7 @@ namespace fc { unsigned_int size; fc::raw::unpack( s, size, _max_depth ); value.clear(); FC_ASSERT( size.value*sizeof(T) < MAX_ARRAY_ALLOC_SIZE ); - value.reserve(size.value); + value.reserve( std::min( size.value, FC_MAX_PREALLOC_SIZE ) ); for( uint32_t i = 0; i < size.value; ++i ) { T tmp; @@ -475,7 +475,7 @@ namespace fc { unsigned_int size; fc::raw::unpack( s, size, _max_depth ); value.clear(); FC_ASSERT( size.value*(sizeof(K)+sizeof(V)) < MAX_ARRAY_ALLOC_SIZE ); - value.reserve(size.value); + value.reserve( std::min( size.value, FC_MAX_PREALLOC_SIZE ) ); for( uint32_t i = 0; i < size.value; ++i ) { std::pair tmp; @@ -530,12 +530,12 @@ namespace fc { --_max_depth; unsigned_int size; fc::raw::unpack( s, size, _max_depth ); FC_ASSERT( size.value*sizeof(T) < MAX_ARRAY_ALLOC_SIZE ); - value.resize(size.value); - auto itr = value.begin(); - auto end = value.end(); - while( itr != end ) { - fc::raw::unpack( s, *itr, _max_depth ); - ++itr; + value.resize( std::min( size.value, FC_MAX_PREALLOC_SIZE ) ); + for( uint64_t i = 0; i < size; i++ ) + { + if( i >= value.size() ) + value.resize( std::min( 2*value.size(), size.value ) ); + unpack( s, value[i], _max_depth ); } } @@ -558,12 +558,12 @@ namespace fc { --_max_depth; unsigned_int size; fc::raw::unpack( s, size, _max_depth ); FC_ASSERT( size.value*sizeof(T) < MAX_ARRAY_ALLOC_SIZE ); - value.resize(size.value); - auto itr = value.begin(); - auto end = value.end(); - while( itr != end ) { - fc::raw::unpack( s, *itr, _max_depth ); - ++itr; + value.resize( std::min( size.value, FC_MAX_PREALLOC_SIZE ) ); + for( uint64_t i = 0; i < size; i++ ) + { + if( i >= value.size() ) + value.resize( std::min( 2*value.size(), size.value ) ); + unpack( s, value[i], _max_depth ); } } diff --git a/include/fc/io/raw_variant.hpp b/include/fc/io/raw_variant.hpp index 59b93d953..bf4ad08e0 100644 --- a/include/fc/io/raw_variant.hpp +++ b/include/fc/io/raw_variant.hpp @@ -143,9 +143,9 @@ namespace fc { namespace raw { --_max_depth; unsigned_int vs; unpack( s, vs, _max_depth ); - + FC_ASSERT( vs.value*sizeof(variant_object::entry) < MAX_ARRAY_ALLOC_SIZE ); mutable_variant_object mvo; - mvo.reserve(vs.value); + mvo.reserve( std::min( vs.value, FC_MAX_PREALLOC_SIZE ) ); for( uint32_t i = 0; i < vs.value; ++i ) { fc::string key; diff --git a/tests/serialization_test.cpp b/tests/serialization_test.cpp index e1e9a54a0..1e0165cdc 100644 --- a/tests/serialization_test.cpp +++ b/tests/serialization_test.cpp @@ -102,4 +102,24 @@ BOOST_AUTO_TEST_CASE( nested_objects_test ) } FC_CAPTURE_LOG_AND_RETHROW ( (0) ) } +BOOST_AUTO_TEST_CASE( unpack_recursion_test ) +{ + try + { + std::stringstream ss; + int recursion_level = 100000; + uint64_t allocation_per_level = 500000; + + for ( int i = 0; i < recursion_level; i++ ) + { + fc::raw::pack( ss, fc::unsigned_int( allocation_per_level ) ); + fc::raw::pack( ss, static_cast< uint8_t >( fc::variant::array_type ) ); + } + + std::vector< fc::variant > v; + BOOST_REQUIRE_THROW( fc::raw::unpack( ss, v ), fc::assert_exception ); + } + FC_LOG_AND_RETHROW(); +} + BOOST_AUTO_TEST_SUITE_END()