未验证 提交 905e7c85 编写于 作者: W wanderingbort 提交者: GitHub

Merge pull request #4339 from EOSIO/feature/1.0.7-security-omnibus

Consolidated Security Fixes for 1.0.7
......@@ -63,6 +63,7 @@ tests/plugin_test
unittests/unit_test
doxygen
eos.doxygen
wallet.json
witness_node_data_dir
......
......@@ -16,6 +16,9 @@ using namespace boost;
namespace eosio { namespace chain {
const size_t abi_serializer::max_recursion_depth;
fc::microseconds abi_serializer::max_serialization_time = fc::microseconds(15*1000); // 15 ms
using boost::algorithm::ends_with;
using std::string;
......@@ -238,21 +241,26 @@ namespace eosio { namespace chain {
return type;
}
void abi_serializer::_binary_to_variant(const type_name& type, fc::datastream<const char *>& stream,
fc::mutable_variant_object& obj, size_t recursion_depth)const {
FC_ASSERT( ++recursion_depth < max_recursion_depth, "recursive definition, max_recursion_depth" );
void abi_serializer::_binary_to_variant( const type_name& type, fc::datastream<const char *>& stream,
fc::mutable_variant_object& obj, size_t recursion_depth,
const fc::time_point& deadline )const
{
FC_ASSERT( ++recursion_depth < max_recursion_depth, "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) );
FC_ASSERT( fc::time_point::now() < deadline, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) );
const auto& st = get_struct(type);
if( st.base != type_name() ) {
_binary_to_variant(resolve_type(st.base), stream, obj, recursion_depth);
_binary_to_variant(resolve_type(st.base), stream, obj, recursion_depth, deadline);
}
for( const auto& field : st.fields ) {
obj( field.name, _binary_to_variant(resolve_type(field.type), stream, recursion_depth) );
obj( field.name, _binary_to_variant(resolve_type(field.type), stream, recursion_depth, deadline) );
}
}
fc::variant abi_serializer::_binary_to_variant(const type_name& type, fc::datastream<const char *>& stream, size_t recursion_depth)const
fc::variant abi_serializer::_binary_to_variant( const type_name& type, fc::datastream<const char *>& stream,
size_t recursion_depth, const fc::time_point& deadline )const
{
FC_ASSERT( ++recursion_depth < max_recursion_depth, "recursive definition, max_recursion_depth" );
FC_ASSERT( ++recursion_depth < max_recursion_depth, "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) );
FC_ASSERT( fc::time_point::now() < deadline, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) );
type_name rtype = resolve_type(type);
auto ftype = fundamental_type(rtype);
auto btype = built_in_types.find(ftype );
......@@ -263,31 +271,41 @@ namespace eosio { namespace chain {
fc::unsigned_int size;
fc::raw::unpack(stream, size);
vector<fc::variant> vars;
vars.resize(size);
for (auto& var : vars) {
var = _binary_to_variant(ftype, stream, recursion_depth);
for( decltype(size.value) i = 0; i < size; ++i ) {
auto v = _binary_to_variant(ftype, stream, recursion_depth, deadline);
FC_ASSERT( !v.is_null(), "Invalid packed array" );
vars.emplace_back(std::move(v));
}
FC_ASSERT( vars.size() == size.value,
"packed size does not match unpacked array size, packed size ${p} actual size ${a}",
("p", size)("a", vars.size()) );
return fc::variant( std::move(vars) );
} else if ( is_optional(rtype) ) {
char flag;
fc::raw::unpack(stream, flag);
return flag ? _binary_to_variant(ftype, stream, recursion_depth) : fc::variant();
return flag ? _binary_to_variant(ftype, stream, recursion_depth, deadline) : fc::variant();
}
fc::mutable_variant_object mvo;
_binary_to_variant(rtype, stream, mvo, recursion_depth);
_binary_to_variant(rtype, stream, mvo, recursion_depth, deadline);
FC_ASSERT( mvo.size() > 0, "Unable to unpack stream ${type}", ("type", type) );
return fc::variant( std::move(mvo) );
}
fc::variant abi_serializer::_binary_to_variant(const type_name& type, const bytes& binary, size_t recursion_depth)const{
FC_ASSERT( ++recursion_depth < max_recursion_depth, "recursive definition, max_recursion_depth" );
fc::variant abi_serializer::_binary_to_variant( const type_name& type, const bytes& binary,
size_t recursion_depth, const fc::time_point& deadline )const
{
FC_ASSERT( ++recursion_depth < max_recursion_depth, "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) );
FC_ASSERT( fc::time_point::now() < deadline, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) );
fc::datastream<const char*> ds( binary.data(), binary.size() );
return _binary_to_variant(type, ds, recursion_depth);
return _binary_to_variant(type, ds, recursion_depth, deadline);
}
void abi_serializer::_variant_to_binary(const type_name& type, const fc::variant& var, fc::datastream<char *>& ds, size_t recursion_depth)const
void abi_serializer::_variant_to_binary( const type_name& type, const fc::variant& var, fc::datastream<char *>& ds,
size_t recursion_depth, const fc::time_point& deadline )const
{ try {
FC_ASSERT( ++recursion_depth < max_recursion_depth, "recursive definition, max_recursion_depth" );
FC_ASSERT( ++recursion_depth < max_recursion_depth, "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) );
FC_ASSERT( fc::time_point::now() < deadline, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) );
auto rtype = resolve_type(type);
auto btype = built_in_types.find(fundamental_type(rtype));
......@@ -297,7 +315,7 @@ namespace eosio { namespace chain {
vector<fc::variant> vars = var.get_array();
fc::raw::pack(ds, (fc::unsigned_int)vars.size());
for (const auto& var : vars) {
_variant_to_binary(fundamental_type(rtype), var, ds, recursion_depth);
_variant_to_binary(fundamental_type(rtype), var, ds, recursion_depth, deadline);
}
} else {
const auto& st = get_struct(rtype);
......@@ -306,14 +324,14 @@ namespace eosio { namespace chain {
const auto& vo = var.get_object();
if( st.base != type_name() ) {
_variant_to_binary(resolve_type(st.base), var, ds, recursion_depth);
_variant_to_binary(resolve_type(st.base), var, ds, recursion_depth, deadline);
}
for( const auto& field : st.fields ) {
if( vo.contains( string(field.name).c_str() ) ) {
_variant_to_binary(field.type, vo[field.name], ds, recursion_depth);
_variant_to_binary(field.type, vo[field.name], ds, recursion_depth, deadline);
}
else {
_variant_to_binary(field.type, fc::variant(), ds, recursion_depth);
_variant_to_binary(field.type, fc::variant(), ds, recursion_depth, deadline);
/// TODO: default construct field and write it out
FC_THROW( "Missing '${f}' in variant object", ("f",field.name) );
}
......@@ -330,9 +348,9 @@ namespace eosio { namespace chain {
if (va.size() > 0) {
for( const auto& field : st.fields ) {
if( va.size() > i )
_variant_to_binary(field.type, va[i], ds, recursion_depth);
_variant_to_binary(field.type, va[i], ds, recursion_depth, deadline);
else
_variant_to_binary(field.type, fc::variant(), ds, recursion_depth);
_variant_to_binary(field.type, fc::variant(), ds, recursion_depth, deadline);
++i;
}
}
......@@ -340,15 +358,18 @@ namespace eosio { namespace chain {
}
} FC_CAPTURE_AND_RETHROW( (type)(var) ) }
bytes abi_serializer::_variant_to_binary(const type_name& type, const fc::variant& var, size_t recursion_depth)const { try {
FC_ASSERT( ++recursion_depth < max_recursion_depth, "recursive definition, max_recursion_depth" );
bytes abi_serializer::_variant_to_binary( const type_name& type, const fc::variant& var,
size_t recursion_depth, const fc::time_point& deadline )const
{ try {
FC_ASSERT( ++recursion_depth < max_recursion_depth, "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) );
FC_ASSERT( fc::time_point::now() < deadline, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) );
if( !is_type(type) ) {
return var.as<bytes>();
}
bytes temp( 1024*1024 );
fc::datastream<char*> ds(temp.data(), temp.size() );
_variant_to_binary(type, var, ds, recursion_depth);
_variant_to_binary(type, var, ds, recursion_depth, deadline);
temp.resize(ds.tellp());
return temp;
} FC_CAPTURE_AND_RETHROW( (type)(var) ) }
......
......@@ -57,6 +57,7 @@ struct controller_impl {
controller::config conf;
chain_id_type chain_id;
bool replaying = false;
bool in_trx_requiring_checks = false; ///< if true, checks that are normally skipped on replay (e.g. auth checks) cannot be skipped
typedef pair<scope_name,action_name> handler_key;
map< account_name, map<handler_key, apply_handler> > apply_handlers;
......@@ -532,6 +533,11 @@ struct controller_impl {
signed_transaction dtrx;
fc::raw::unpack(ds,static_cast<transaction&>(dtrx) );
auto reset_in_trx_requiring_checks = fc::make_scoped_exit([old_value=in_trx_requiring_checks,this](){
in_trx_requiring_checks = old_value;
});
in_trx_requiring_checks = true;
transaction_context trx_context( self, dtrx, gto.trx_id );
trx_context.deadline = deadline;
trx_context.billed_cpu_time_us = billed_cpu_time_us;
......@@ -760,6 +766,10 @@ struct controller_impl {
try {
auto onbtrx = std::make_shared<transaction_metadata>( get_on_block_transaction() );
auto reset_in_trx_requiring_checks = fc::make_scoped_exit([old_value=in_trx_requiring_checks,this](){
in_trx_requiring_checks = old_value;
});
in_trx_requiring_checks = true;
push_transaction( onbtrx, fc::time_point::maximum(), true, self.get_global_properties().configuration.min_transaction_cpu_usage );
} catch( const boost::interprocess::bad_alloc& e ) {
elog( "on block transaction failed due to a bad allocation" );
......@@ -1395,7 +1405,7 @@ optional<producer_schedule_type> controller::proposed_producers()const {
}
bool controller::skip_auth_check()const {
return my->replaying && !my->conf.force_all_checks;
return my->replaying && !my->conf.force_all_checks && !my->in_trx_requiring_checks;
}
bool controller::contracts_console()const {
......
......@@ -46,6 +46,8 @@ namespace eosio { namespace testing {
cfg.genesis.initial_timestamp = fc::time_point::from_iso_string("2020-01-01T00:00:00.000");
cfg.genesis.initial_key = get_public_key( config::system_account_name, "active" );
abi_serializer::set_max_serialization_time(fc::microseconds(100*1000)); // 100ms for slow test machines
for(int i = 0; i < boost::unit_test::framework::master_test_suite().argc; ++i) {
if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--binaryen"))
cfg.wasm_runtime = chain::wasm_interface::vm_type::binaryen;
......
......@@ -493,15 +493,16 @@ namespace WASM
if (numBodyBytes >= max_size)
throw FatalSerializationException(std::string("Function body too large"));
if (numLocalSets >= 1024)
throw FatalSerializationException(std::string("too many locals"));
throw FatalSerializationException(std::string("too many local sets"));
size_t locals_accum = numBodyBytes;
for(Uptr setIndex = 0;setIndex < numLocalSets;++setIndex)
{
LocalSet localSet;
serialize(bodyStream,localSet);
if( localSet.num > 8024 )
throw FatalSerializationException( "localSet.num too large" );
locals_accum += localSet.num*4;
if( locals_accum >= max_size )
throw FatalSerializationException( "too many locals" );
for(Uptr index = 0;index < localSet.num;++index) { functionDef.nonParameterLocalTypes.push_back(localSet.type); }
}
......
......@@ -128,6 +128,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
"the location of the blocks directory (absolute path or relative to application data dir)")
("checkpoint", bpo::value<vector<string>>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
("wasm-runtime", bpo::value<eosio::chain::wasm_interface::vm_type>()->value_name("wavm/binaryen"), "Override default WASM runtime")
("abi-serializer-max-time-ms", bpo::value<uint32_t>(), "Override default maximum ABI serialization time allowed in ms")
("chain-state-db-size-mb", bpo::value<uint64_t>()->default_value(config::default_state_size / (1024 * 1024)), "Maximum size (in MB) of the chain state database")
("reversible-blocks-db-size-mb", bpo::value<uint64_t>()->default_value(config::default_reversible_cache_size / (1024 * 1024)), "Maximum size (in MB) of the reversible blocks database")
("contracts-console", bpo::bool_switch()->default_value(false),
......@@ -265,6 +266,9 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
if(options.count("wasm-runtime"))
my->wasm_runtime = options.at("wasm-runtime").as<vm_type>();
if(options.count("abi-serializer-max-time-ms"))
abi_serializer::set_max_serialization_time(fc::microseconds(options.at("abi-serializer-max-time-ms").as<uint32_t>() * 1000));
my->chain_config->blocks_dir = my->blocks_dir;
my->chain_config->state_dir = app().data_dir() / config::default_state_dir_name;
my->chain_config->read_only = my->readonly;
......
......@@ -1865,7 +1865,8 @@ namespace eosio {
peer_block_state entry = {blkid,0,true,true,fc::time_point()};
try {
b = cc.fetch_block_by_id(blkid);
entry.block_num = b->block_num();
if(b)
entry.block_num = b->block_num();
} catch (const assert_exception &ex) {
ilog( "caught assert on fetch_block_by_id, ${ex}",("ex",ex.what()));
// keep going, client can ask another peer
......
......@@ -354,7 +354,7 @@ struct txn_test_gen_plugin_impl {
int32_t txn_reference_block_lag;
abi_serializer eosio_token_serializer = fc::json::from_string(eosio_token_abi).as<abi_def>();
abi_serializer eosio_token_serializer{fc::json::from_string(eosio_token_abi).as<abi_def>()};
};
txn_test_gen_plugin::txn_test_gen_plugin() {}
......
......@@ -13,6 +13,7 @@
#include <fc/io/json.hpp>
#include <fc/exception/exception.hpp>
#include <fc/log/logger.hpp>
#include <fc/scoped_exit.hpp>
#include <eosio/chain/contract_types.hpp>
#include <eosio/chain/abi_serializer.hpp>
......@@ -512,7 +513,7 @@ BOOST_AUTO_TEST_CASE(uint_types)
auto var = fc::json::from_string(test_data);
verify_byte_round_trip_conversion(abi, "transfer", var);
verify_byte_round_trip_conversion(abi_serializer{abi}, "transfer", var);
} FC_LOG_AND_RETHROW() }
......@@ -2000,7 +2001,7 @@ BOOST_AUTO_TEST_CASE(general)
)=====";
auto var = fc::json::from_string(my_other);
verify_byte_round_trip_conversion(abi, "A", var);
verify_byte_round_trip_conversion(abi_serializer{abi}, "A", var);
} FC_LOG_AND_RETHROW() }
......@@ -3300,4 +3301,107 @@ BOOST_AUTO_TEST_CASE(abi_account_name_in_eosio_abi)
} FC_LOG_AND_RETHROW() }
// Infinite recursion of abi_serializer is_type
BOOST_AUTO_TEST_CASE(abi_is_type_recursion)
{
try {
const char* abi_str = R"=====(
{
"types": [
{
"new_type_name": "a[]",
"type": "a[][]",
},
],
"structs": [
{
"name": "a[]",
"base": "",
"fields": []
},
{
"name": "hi",
"base": "",
"fields": [{
"name": "user",
"type": "name"
}
]
}
],
"actions": [{
"name": "hi",
"type": "hi",
"ricardian_contract": ""
}
],
"tables": []
}
)=====";
BOOST_CHECK_THROW( abi_serializer abis(fc::json::from_string(abi_str).as<abi_def>()), fc::exception );
} FC_LOG_AND_RETHROW()
}
// Infinite recursion of abi_serializer in struct definitions
BOOST_AUTO_TEST_CASE(abi_recursive_structs)
{
try {
const char* abi_str = R"=====(
{
"types": [],
"structs": [
{
"name": "a"
"base": "",
"fields": [
{
"name": "user",
"type": "b"
}
]
},
{
"name": "b"
"base": "",
"fields": [
{
"name": "user",
"type": "a"
}
]
},
{
"name": "hi",
"base": "",
"fields": [{
"name": "user",
"type": "name"
},
{
"name": "arg2",
"type": "a"
}
]
}
],
"actions": [{
"name": "hi",
"type": "hi",
"ricardian_contract": ""
}
],
"tables": []
}
)=====";
abi_serializer abis(fc::json::from_string(abi_str).as<abi_def>());
string hi_data = "{\"user\":\"eosio\",\"arg2\":{\"user\":\"1\"}}";
auto bin = abis.variant_to_binary("hi", fc::json::from_string(hi_data));
BOOST_CHECK_THROW( abis.binary_to_variant("hi", bin);, fc::exception );
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_SUITE_END()
......@@ -65,7 +65,7 @@ BOOST_AUTO_TEST_SUITE(tic_tac_toe_tests)
BOOST_AUTO_TEST_CASE( tic_tac_toe_game ) try {
TESTER chain;
abi_serializer abi_ser = json::from_string(tic_tac_toe_abi).as<abi_def>();
abi_serializer abi_ser{json::from_string(tic_tac_toe_abi).as<abi_def>()};
chain.create_account(N(tic.tac.toe));
chain.produce_blocks(10);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册