Skip to content

Commit

Permalink
Merge pull request EOSIO#4036 from larryk85/fix/eos4031
Browse files Browse the repository at this point in the history
Limit nested structures in wasm
  • Loading branch information
larryk85 authored Jun 11, 2018
2 parents 72bb132 + 43e3a96 commit 012dc01
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 11 deletions.
2 changes: 1 addition & 1 deletion libraries/chain/eosio_contract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ void apply_eosio_setcode(apply_context& context) {

if( act.code.size() > 0 ) {
code_id = fc::sha256::hash( act.code.data(), (uint32_t)act.code.size() );
wasm_interface::validate(act.code);
wasm_interface::validate(context.control, act.code);
}

const auto& account = db.get<account_object,by_name>(act.account);
Expand Down
32 changes: 26 additions & 6 deletions libraries/chain/include/eosio/chain/wasm_eosio_validation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <fc/exception/exception.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/chain/controller.hpp>
#include <eosio/chain/wasm_eosio_binary_ops.hpp>
#include <functional>
#include <vector>
Expand Down Expand Up @@ -99,15 +100,33 @@ namespace eosio { namespace chain { namespace wasm_validations {
}
};

struct nested_validator {
static constexpr bool kills = false;
static constexpr bool post = false;
static bool disabled;
static uint16_t depth;
static void init(bool disable) { disabled = disable; depth = 0; }
static void accept( wasm_ops::instr* inst, wasm_ops::visitor_arg& arg ) {
if (!disabled) {
if ( inst->get_code() == wasm_ops::end_code && depth > 0 ) {
depth--;
return;
}
depth++;
EOS_ASSERT(depth < 1024, wasm_execution_error, "Nested depth exceeded");
}
}
};

// add opcode specific constraints here
// so far we only black list
struct op_constrainers : wasm_ops::op_types<blacklist_validator> {
using block_t = wasm_ops::block <whitelist_validator>;
using loop_t = wasm_ops::loop <whitelist_validator>;
using if__t = wasm_ops::if_ <whitelist_validator>;
using else__t = wasm_ops::else_ <whitelist_validator>;
using block_t = wasm_ops::block <whitelist_validator, nested_validator>;
using loop_t = wasm_ops::loop <whitelist_validator, nested_validator>;
using if__t = wasm_ops::if_ <whitelist_validator, nested_validator>;
using else__t = wasm_ops::else_ <whitelist_validator, nested_validator>;

using end_t = wasm_ops::end <whitelist_validator>;
using end_t = wasm_ops::end <whitelist_validator, nested_validator>;
using unreachable_t = wasm_ops::unreachable <whitelist_validator>;
using br_t = wasm_ops::br <whitelist_validator>;
using br_if_t = wasm_ops::br_if <whitelist_validator>;
Expand Down Expand Up @@ -311,8 +330,9 @@ namespace eosio { namespace chain { namespace wasm_validations {
maximum_function_stack_visitor,
ensure_apply_exported_visitor>;
public:
wasm_binary_validation( IR::Module& mod ) : _module( &mod ) {
wasm_binary_validation( const eosio::chain::controller& control, IR::Module& mod ) : _module( &mod ) {
// initialize validators here
nested_validator::init(!control.is_producing_block());
}

void validate() {
Expand Down
3 changes: 2 additions & 1 deletion libraries/chain/include/eosio/chain/wasm_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace eosio { namespace chain {

class apply_context;
class wasm_runtime_interface;
class controller;

struct wasm_exit {
int32_t code = 0;
Expand Down Expand Up @@ -58,7 +59,7 @@ namespace eosio { namespace chain {
~wasm_interface();

//validates code -- does a WASM validation pass and checks the wasm against EOSIO specific constraints
static void validate(const bytes& code);
static void validate(const controller& control, const bytes& code);

//Calls apply or error on a given code
void apply(const digest_type& code_id, const shared_string& code, apply_context& context);
Expand Down
2 changes: 2 additions & 0 deletions libraries/chain/wasm_eosio_validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,6 @@ void ensure_apply_exported_visitor::validate( const IR::Module& m ) {
FC_THROW_EXCEPTION(wasm_execution_error, "Smart contract's apply function not exported; non-existent; or wrong type");
}

uint16_t nested_validator::depth = 0;
bool nested_validator::disabled = false;
}}} // namespace eosio chain validation
4 changes: 2 additions & 2 deletions libraries/chain/wasm_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace eosio { namespace chain {

wasm_interface::~wasm_interface() {}

void wasm_interface::validate(const bytes& code) {
void wasm_interface::validate(const controller& control, const bytes& code) {
Module module;
try {
Serialization::MemoryInputStream stream((U8*)code.data(), code.size());
Expand All @@ -42,7 +42,7 @@ namespace eosio { namespace chain {
EOS_ASSERT(false, wasm_serialization_error, e.message.c_str());
}

wasm_validations::wasm_binary_validation validator(module);
wasm_validations::wasm_binary_validation validator(control, module);
validator.validate();

root_resolver resolver(true);
Expand Down
2 changes: 1 addition & 1 deletion unittests/contracts/test_wasts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -599,4 +599,4 @@ static const char memory_growth_memset_test[] = R"=====(
)
)
)
)=====";
)=====";
107 changes: 107 additions & 0 deletions unittests/wasm_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,113 @@ BOOST_FIXTURE_TEST_CASE( imports, TESTER ) try {

} FC_LOG_AND_RETHROW()

BOOST_FIXTURE_TEST_CASE( nested_limit_test, TESTER ) try {
produce_blocks(2);

create_accounts( {N(nested)} );
produce_block();

// nested loops
{
std::stringstream ss;
ss << "(module (export \"apply\" (func $apply)) (func $apply (param $0 i64) (param $1 i64) (param $2 i64)";
for(unsigned int i = 0; i < 1023; ++i)
ss << "(loop (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 1023; ++i)
ss << ")";
ss << "))";
set_code(N(nested), ss.str().c_str());
}
{
std::stringstream ss;
ss << "(module (export \"apply\" (func $apply)) (func $apply (param $0 i64) (param $1 i64) (param $2 i64)";
for(unsigned int i = 0; i < 1024; ++i)
ss << "(loop (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 1024; ++i)
ss << ")";
ss << "))";
BOOST_CHECK_THROW(set_code(N(nested), ss.str().c_str()), eosio::chain::wasm_execution_error);
}

// nested blocks
{
std::stringstream ss;
ss << "(module (export \"apply\" (func $apply)) (func $apply (param $0 i64) (param $1 i64) (param $2 i64)";
for(unsigned int i = 0; i < 1023; ++i)
ss << "(block (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 1023; ++i)
ss << ")";
ss << "))";
set_code(N(nested), ss.str().c_str());
}
{
std::stringstream ss;
ss << "(module (export \"apply\" (func $apply)) (func $apply (param $0 i64) (param $1 i64) (param $2 i64)";
for(unsigned int i = 0; i < 1024; ++i)
ss << "(block (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 1024; ++i)
ss << ")";
ss << "))";
BOOST_CHECK_THROW(set_code(N(nested), ss.str().c_str()), eosio::chain::wasm_execution_error);
}
// nested ifs
{
std::stringstream ss;
ss << "(module (export \"apply\" (func $apply)) (func $apply (param $0 i64) (param $1 i64) (param $2 i64)";
for(unsigned int i = 0; i < 1023; ++i)
ss << "(if (i32.wrap/i64 (get_local $0)) (then (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 1023; ++i)
ss << "))";
ss << "))";
set_code(N(nested), ss.str().c_str());
}
{
std::stringstream ss;
ss << "(module (export \"apply\" (func $apply)) (func $apply (param $0 i64) (param $1 i64) (param $2 i64)";
for(unsigned int i = 0; i < 1024; ++i)
ss << "(if (i32.wrap/i64 (get_local $0)) (then (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 1024; ++i)
ss << "))";
ss << "))";
BOOST_CHECK_THROW(set_code(N(nested), ss.str().c_str()), eosio::chain::wasm_execution_error);
}
// mixed nested
{
std::stringstream ss;
ss << "(module (export \"apply\" (func $apply)) (func $apply (param $0 i64) (param $1 i64) (param $2 i64)";
for(unsigned int i = 0; i < 223; ++i)
ss << "(if (i32.wrap/i64 (get_local $0)) (then (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 400; ++i)
ss << "(block (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 400; ++i)
ss << "(loop (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 800; ++i)
ss << ")";
for(unsigned int i = 0; i < 223; ++i)
ss << "))";
ss << "))";
set_code(N(nested), ss.str().c_str());
}
{
std::stringstream ss;
ss << "(module (export \"apply\" (func $apply)) (func $apply (param $0 i64) (param $1 i64) (param $2 i64)";
for(unsigned int i = 0; i < 224; ++i)
ss << "(if (i32.wrap/i64 (get_local $0)) (then (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 400; ++i)
ss << "(block (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 400; ++i)
ss << "(loop (drop (i32.const " << i << "))";
for(unsigned int i = 0; i < 800; ++i)
ss << ")";
for(unsigned int i = 0; i < 224; ++i)
ss << "))";
ss << "))";
BOOST_CHECK_THROW(set_code(N(nested), ss.str().c_str()), eosio::chain::wasm_execution_error);
}

} FC_LOG_AND_RETHROW()


BOOST_FIXTURE_TEST_CASE( lotso_globals, TESTER ) try {
produce_blocks(2);

Expand Down

0 comments on commit 012dc01

Please sign in to comment.