提交 a7d76dc9 编写于 作者: M Matt Witherspoon

Require “apply” function always exported in WASM

Require, via validation, that all contracts export a valid “apply” function. This will stave off runtime divergence based on behavior of calling a non existent apply (or an apply function that has the wrong signature)

Also fix the troublesome unit tests that violate this
上级 d3e289a5
...@@ -38,6 +38,10 @@ namespace eosio { namespace chain { namespace wasm_validations { ...@@ -38,6 +38,10 @@ namespace eosio { namespace chain { namespace wasm_validations {
static void validate( const IR::Module& m ); static void validate( const IR::Module& m );
}; };
struct ensure_apply_exported_visitor {
static void validate( const IR::Module& m );
};
using wasm_validate_func = std::function<void(IR::Module&)>; using wasm_validate_func = std::function<void(IR::Module&)>;
...@@ -303,7 +307,8 @@ namespace eosio { namespace chain { namespace wasm_validations { ...@@ -303,7 +307,8 @@ namespace eosio { namespace chain { namespace wasm_validations {
data_segments_validation_visitor, data_segments_validation_visitor,
tables_validation_visitor, tables_validation_visitor,
globals_validation_visitor, globals_validation_visitor,
maximum_function_stack_visitor>; maximum_function_stack_visitor,
ensure_apply_exported_visitor>;
public: public:
wasm_binary_validation( IR::Module& mod ) : _module( &mod ) { wasm_binary_validation( IR::Module& mod ) : _module( &mod ) {
// initialize validators here // initialize validators here
......
...@@ -71,7 +71,22 @@ void maximum_function_stack_visitor::validate( const IR::Module& m ) { ...@@ -71,7 +71,22 @@ void maximum_function_stack_visitor::validate( const IR::Module& m ) {
FC_THROW_EXCEPTION(wasm_execution_error, "Smart contract function has more than ${k} bytes of stack usage", FC_THROW_EXCEPTION(wasm_execution_error, "Smart contract function has more than ${k} bytes of stack usage",
("k", wasm_constraints::maximum_func_local_bytes)); ("k", wasm_constraints::maximum_func_local_bytes));
} }
}
void ensure_apply_exported_visitor::validate( const IR::Module& m ) {
bool found_it = false;
for(const Export& exprt : m.exports) {
if(exprt.name != "apply" && exprt.kind != ObjectKind::function)
continue;
if(m.types[m.functions.getType(exprt.index).index] == FunctionType::get(ResultType::none, {ValueType::i64, ValueType::i64})) {
found_it = true;
break;
}
}
if(!found_it)
FC_THROW_EXCEPTION(wasm_execution_error, "Smart contract's apply function not exported; non-existent; or wrong type");
} }
}}} // namespace eosio chain validation }}} // namespace eosio chain validation
...@@ -117,6 +117,7 @@ static const char too_big_memory_wast[] = R"=====( ...@@ -117,6 +117,7 @@ static const char too_big_memory_wast[] = R"=====(
static const char valid_sparse_table[] = R"=====( static const char valid_sparse_table[] = R"=====(
(module (module
(table 1024 anyfunc) (table 1024 anyfunc)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64)) (func $apply (param $0 i64) (param $1 i64))
(elem (i32.const 0) $apply) (elem (i32.const 0) $apply)
(elem (i32.const 1022) $apply $apply) (elem (i32.const 1022) $apply $apply)
...@@ -126,6 +127,7 @@ static const char valid_sparse_table[] = R"=====( ...@@ -126,6 +127,7 @@ static const char valid_sparse_table[] = R"=====(
static const char too_big_table[] = R"=====( static const char too_big_table[] = R"=====(
(module (module
(table 1025 anyfunc) (table 1025 anyfunc)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64)) (func $apply (param $0 i64) (param $1 i64))
(elem (i32.const 0) $apply) (elem (i32.const 0) $apply)
(elem (i32.const 1022) $apply $apply) (elem (i32.const 1022) $apply $apply)
...@@ -135,6 +137,8 @@ static const char too_big_table[] = R"=====( ...@@ -135,6 +137,8 @@ static const char too_big_table[] = R"=====(
static const char memory_init_borderline[] = R"=====( static const char memory_init_borderline[] = R"=====(
(module (module
(memory $0 16) (memory $0 16)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64))
(data (i32.const 65532) "sup!") (data (i32.const 65532) "sup!")
) )
)====="; )=====";
...@@ -142,6 +146,8 @@ static const char memory_init_borderline[] = R"=====( ...@@ -142,6 +146,8 @@ static const char memory_init_borderline[] = R"=====(
static const char memory_init_toolong[] = R"=====( static const char memory_init_toolong[] = R"=====(
(module (module
(memory $0 16) (memory $0 16)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64))
(data (i32.const 65533) "sup!") (data (i32.const 65533) "sup!")
) )
)====="; )=====";
...@@ -149,6 +155,8 @@ static const char memory_init_toolong[] = R"=====( ...@@ -149,6 +155,8 @@ static const char memory_init_toolong[] = R"=====(
static const char memory_init_negative[] = R"=====( static const char memory_init_negative[] = R"=====(
(module (module
(memory $0 16) (memory $0 16)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64))
(data (i32.const -1) "sup!") (data (i32.const -1) "sup!")
) )
)====="; )=====";
...@@ -157,6 +165,8 @@ static const char memory_table_import[] = R"=====( ...@@ -157,6 +165,8 @@ static const char memory_table_import[] = R"=====(
(module (module
(table (import "foo" "table") 10 anyfunc) (table (import "foo" "table") 10 anyfunc)
(memory (import "nom" "memory") 0) (memory (import "nom" "memory") 0)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64))
) )
)====="; )=====";
...@@ -389,3 +399,16 @@ static const std::vector<uint8_t> global_protection_some_set_wasm{ ...@@ -389,3 +399,16 @@ static const std::vector<uint8_t> global_protection_some_set_wasm{
0x24, 0x01, //set global 1 0x24, 0x01, //set global 1
0x0b //end 0x0b //end
}; };
static const char no_apply_wast[] = R"=====(
(module
(func $apply (param $0 i64) (param $1 i64))
)
)=====";
static const char apply_wrong_signature_wast[] = R"=====(
(module
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 f64))
)
)=====";
\ No newline at end of file
...@@ -565,7 +565,7 @@ BOOST_FIXTURE_TEST_CASE( lotso_globals, tester ) try { ...@@ -565,7 +565,7 @@ BOOST_FIXTURE_TEST_CASE( lotso_globals, tester ) try {
produce_block(); produce_block();
std::stringstream ss; std::stringstream ss;
ss << "(module "; ss << "(module (export \"apply\" (func $apply)) (func $apply (param $0 i64) (param $1 i64))";
for(unsigned int i = 0; i < 85; ++i) for(unsigned int i = 0; i < 85; ++i)
ss << "(global $g" << i << " (mut i32) (i32.const 0))" << "(global $g" << i+100 << " (mut i64) (i64.const 0))"; ss << "(global $g" << i << " (mut i32) (i32.const 0))" << "(global $g" << i+100 << " (mut i64) (i64.const 0))";
//that gives us 1020 bytes of mutable globals //that gives us 1020 bytes of mutable globals
...@@ -593,17 +593,16 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try { ...@@ -593,17 +593,16 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try {
create_accounts( {N(offsets)} ); create_accounts( {N(offsets)} );
produce_block(); produce_block();
//floats not tested since they are blocked in the serializer before eosio_constraints
vector<string> loadops = { vector<string> loadops = {
"i32.load", "i64.load", /* "f32.load", "f64.load",*/ "i32.load8_s", "i32.load8_u", "i32.load", "i64.load", "f32.load", "f64.load", "i32.load8_s", "i32.load8_u",
"i32.load16_s", "i32.load16_u", "i64.load8_s", "i64.load8_u", "i64.load16_s", "i32.load16_s", "i32.load16_u", "i64.load8_s", "i64.load8_u", "i64.load16_s",
"i64.load16_u", "i64.load32_s", "i64.load32_u" "i64.load16_u", "i64.load32_s", "i64.load32_u"
}; };
vector<vector<string>> storeops = { vector<vector<string>> storeops = {
{"i32.store", "i32"}, {"i32.store", "i32"},
{"i64.store", "i64"}, {"i64.store", "i64"},
/*{"f32.store", "f32"}, {"f32.store", "f32"},
{"f64.store", "f64"},*/ {"f64.store", "f64"},
{"i32.store8", "i32"}, {"i32.store8", "i32"},
{"i32.store16", "i32"}, {"i32.store16", "i32"},
{"i64.store8", "i64"}, {"i64.store8", "i64"},
...@@ -615,7 +614,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try { ...@@ -615,7 +614,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try {
std::stringstream ss; std::stringstream ss;
ss << "(module (memory $0 " << eosio::chain::wasm_constraints::maximum_linear_memory/(64*1024) << ") (func $apply (param $0 i64) (param $1 i64) "; ss << "(module (memory $0 " << eosio::chain::wasm_constraints::maximum_linear_memory/(64*1024) << ") (func $apply (param $0 i64) (param $1 i64) ";
ss << "(drop (" << s << " offset=" << eosio::chain::wasm_constraints::maximum_linear_memory-2 << " (i32.const 0)))"; ss << "(drop (" << s << " offset=" << eosio::chain::wasm_constraints::maximum_linear_memory-2 << " (i32.const 0)))";
ss << "))"; ss << ") (export \"apply\" (func $apply)) )";
set_code(N(offsets), ss.str().c_str()); set_code(N(offsets), ss.str().c_str());
produce_block(); produce_block();
...@@ -624,7 +623,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try { ...@@ -624,7 +623,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try {
std::stringstream ss; std::stringstream ss;
ss << "(module (memory $0 " << eosio::chain::wasm_constraints::maximum_linear_memory/(64*1024) << ") (func $apply (param $0 i64) (param $1 i64) "; ss << "(module (memory $0 " << eosio::chain::wasm_constraints::maximum_linear_memory/(64*1024) << ") (func $apply (param $0 i64) (param $1 i64) ";
ss << "(" << o[0] << " offset=" << eosio::chain::wasm_constraints::maximum_linear_memory-2 << " (i32.const 0) (" << o[1] << ".const 0))"; ss << "(" << o[0] << " offset=" << eosio::chain::wasm_constraints::maximum_linear_memory-2 << " (i32.const 0) (" << o[1] << ".const 0))";
ss << "))"; ss << ") (export \"apply\" (func $apply)) )";
set_code(N(offsets), ss.str().c_str()); set_code(N(offsets), ss.str().c_str());
produce_block(); produce_block();
...@@ -634,7 +633,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try { ...@@ -634,7 +633,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try {
std::stringstream ss; std::stringstream ss;
ss << "(module (memory $0 " << eosio::chain::wasm_constraints::maximum_linear_memory/(64*1024) << ") (func $apply (param $0 i64) (param $1 i64) "; ss << "(module (memory $0 " << eosio::chain::wasm_constraints::maximum_linear_memory/(64*1024) << ") (func $apply (param $0 i64) (param $1 i64) ";
ss << "(drop (" << s << " offset=" << eosio::chain::wasm_constraints::maximum_linear_memory+4 << " (i32.const 0)))"; ss << "(drop (" << s << " offset=" << eosio::chain::wasm_constraints::maximum_linear_memory+4 << " (i32.const 0)))";
ss << "))"; ss << ") (export \"apply\" (func $apply)) )";
BOOST_CHECK_THROW(set_code(N(offsets), ss.str().c_str()), eosio::chain::wasm_execution_error); BOOST_CHECK_THROW(set_code(N(offsets), ss.str().c_str()), eosio::chain::wasm_execution_error);
produce_block(); produce_block();
...@@ -643,7 +642,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try { ...@@ -643,7 +642,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try {
std::stringstream ss; std::stringstream ss;
ss << "(module (memory $0 " << eosio::chain::wasm_constraints::maximum_linear_memory/(64*1024) << ") (func $apply (param $0 i64) (param $1 i64) "; ss << "(module (memory $0 " << eosio::chain::wasm_constraints::maximum_linear_memory/(64*1024) << ") (func $apply (param $0 i64) (param $1 i64) ";
ss << "(" << o[0] << " offset=" << eosio::chain::wasm_constraints::maximum_linear_memory+4 << " (i32.const 0) (" << o[1] << ".const 0))"; ss << "(" << o[0] << " offset=" << eosio::chain::wasm_constraints::maximum_linear_memory+4 << " (i32.const 0) (" << o[1] << ".const 0))";
ss << "))"; ss << ") (export \"apply\" (func $apply)) )";
BOOST_CHECK_THROW(set_code(N(offsets), ss.str().c_str()), eosio::chain::wasm_execution_error); BOOST_CHECK_THROW(set_code(N(offsets), ss.str().c_str()), eosio::chain::wasm_execution_error);
produce_block(); produce_block();
...@@ -950,6 +949,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try { ...@@ -950,6 +949,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{ {
std::stringstream ss; std::stringstream ss;
ss << "(module "; ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func "; ss << " (func ";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes; i+=4) for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes; i+=4)
ss << "(local i32)"; ss << "(local i32)";
...@@ -961,6 +962,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try { ...@@ -961,6 +962,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{ {
std::stringstream ss; std::stringstream ss;
ss << "(module "; ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func "; ss << " (func ";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes; i+=8) for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes; i+=8)
ss << "(local f64)"; ss << "(local f64)";
...@@ -990,6 +993,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try { ...@@ -990,6 +993,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{ {
std::stringstream ss; std::stringstream ss;
ss << "(module "; ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func "; ss << " (func ";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes+4; i+=4) for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes+4; i+=4)
ss << "(local i32)"; ss << "(local i32)";
...@@ -1003,6 +1008,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try { ...@@ -1003,6 +1008,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{ {
std::stringstream ss; std::stringstream ss;
ss << "(module "; ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func "; ss << " (func ";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes; i+=4) for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes; i+=4)
ss << "(param i32)"; ss << "(param i32)";
...@@ -1029,6 +1036,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try { ...@@ -1029,6 +1036,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{ {
std::stringstream ss; std::stringstream ss;
ss << "(module "; ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func "; ss << " (func ";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes+4; i+=4) for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes+4; i+=4)
ss << "(param i32)"; ss << "(param i32)";
...@@ -1042,6 +1051,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try { ...@@ -1042,6 +1051,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{ {
std::stringstream ss; std::stringstream ss;
ss << "(module "; ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func (param i64) (param f32) "; ss << " (func (param i64) (param f32) ";
for(unsigned int i = 12; i < wasm_constraints::maximum_func_local_bytes; i+=4) for(unsigned int i = 12; i < wasm_constraints::maximum_func_local_bytes; i+=4)
ss << "(local i32)"; ss << "(local i32)";
...@@ -1053,6 +1064,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try { ...@@ -1053,6 +1064,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{ {
std::stringstream ss; std::stringstream ss;
ss << "(module "; ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func (param i64) (param f32) "; ss << " (func (param i64) (param f32) ";
for(unsigned int i = 12; i < wasm_constraints::maximum_func_local_bytes+4; i+=4) for(unsigned int i = 12; i < wasm_constraints::maximum_func_local_bytes+4; i+=4)
ss << "(local f32)"; ss << "(local f32)";
...@@ -1065,4 +1078,17 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try { ...@@ -1065,4 +1078,17 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
} FC_LOG_AND_RETHROW() } FC_LOG_AND_RETHROW()
BOOST_FIXTURE_TEST_CASE( apply_export_and_signature, tester ) try {
produce_blocks(2);
create_accounts( {N(bbb)} );
produce_block();
BOOST_CHECK_THROW(set_code(N(bbb), no_apply_wast), fc::exception);
produce_blocks(1);
BOOST_CHECK_THROW(set_code(N(bbb), apply_wrong_signature_wast), fc::exception);
produce_blocks(1);
} FC_LOG_AND_RETHROW()
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册