提交 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 {
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&)>;
......@@ -303,7 +307,8 @@ namespace eosio { namespace chain { namespace wasm_validations {
data_segments_validation_visitor,
tables_validation_visitor,
globals_validation_visitor,
maximum_function_stack_visitor>;
maximum_function_stack_visitor,
ensure_apply_exported_visitor>;
public:
wasm_binary_validation( IR::Module& mod ) : _module( &mod ) {
// initialize validators here
......
......@@ -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",
("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
......@@ -117,6 +117,7 @@ static const char too_big_memory_wast[] = R"=====(
static const char valid_sparse_table[] = R"=====(
(module
(table 1024 anyfunc)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64))
(elem (i32.const 0) $apply)
(elem (i32.const 1022) $apply $apply)
......@@ -126,6 +127,7 @@ static const char valid_sparse_table[] = R"=====(
static const char too_big_table[] = R"=====(
(module
(table 1025 anyfunc)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64))
(elem (i32.const 0) $apply)
(elem (i32.const 1022) $apply $apply)
......@@ -135,6 +137,8 @@ static const char too_big_table[] = R"=====(
static const char memory_init_borderline[] = R"=====(
(module
(memory $0 16)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64))
(data (i32.const 65532) "sup!")
)
)=====";
......@@ -142,6 +146,8 @@ static const char memory_init_borderline[] = R"=====(
static const char memory_init_toolong[] = R"=====(
(module
(memory $0 16)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64))
(data (i32.const 65533) "sup!")
)
)=====";
......@@ -149,6 +155,8 @@ static const char memory_init_toolong[] = R"=====(
static const char memory_init_negative[] = R"=====(
(module
(memory $0 16)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64))
(data (i32.const -1) "sup!")
)
)=====";
......@@ -157,6 +165,8 @@ static const char memory_table_import[] = R"=====(
(module
(table (import "foo" "table") 10 anyfunc)
(memory (import "nom" "memory") 0)
(export "apply" (func $apply))
(func $apply (param $0 i64) (param $1 i64))
)
)=====";
......@@ -388,4 +398,17 @@ static const std::vector<uint8_t> global_protection_some_set_wasm{
0x20, 0x00, //get local 0
0x24, 0x01, //set global 1
0x0b //end
};
\ No newline at end of file
};
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 {
produce_block();
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)
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
......@@ -593,17 +593,16 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try {
create_accounts( {N(offsets)} );
produce_block();
//floats not tested since they are blocked in the serializer before eosio_constraints
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",
"i64.load16_u", "i64.load32_s", "i64.load32_u"
};
vector<vector<string>> storeops = {
{"i32.store", "i32"},
{"i64.store", "i64"},
/*{"f32.store", "f32"},
{"f64.store", "f64"},*/
{"f32.store", "f32"},
{"f64.store", "f64"},
{"i32.store8", "i32"},
{"i32.store16", "i32"},
{"i64.store8", "i64"},
......@@ -615,7 +614,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try {
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 << "(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());
produce_block();
......@@ -624,7 +623,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try {
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 << "(" << 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());
produce_block();
......@@ -634,7 +633,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try {
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 << "(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);
produce_block();
......@@ -643,7 +642,7 @@ BOOST_FIXTURE_TEST_CASE( offset_check, tester ) try {
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 << "(" << 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);
produce_block();
......@@ -950,6 +949,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{
std::stringstream ss;
ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func ";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes; i+=4)
ss << "(local i32)";
......@@ -961,6 +962,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{
std::stringstream ss;
ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func ";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes; i+=8)
ss << "(local f64)";
......@@ -990,6 +993,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{
std::stringstream ss;
ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func ";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes+4; i+=4)
ss << "(local i32)";
......@@ -1003,6 +1008,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{
std::stringstream ss;
ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func ";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes; i+=4)
ss << "(param i32)";
......@@ -1029,6 +1036,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{
std::stringstream ss;
ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func ";
for(unsigned int i = 0; i < wasm_constraints::maximum_func_local_bytes+4; i+=4)
ss << "(param i32)";
......@@ -1042,6 +1051,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{
std::stringstream ss;
ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func (param i64) (param f32) ";
for(unsigned int i = 12; i < wasm_constraints::maximum_func_local_bytes; i+=4)
ss << "(local i32)";
......@@ -1053,6 +1064,8 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
{
std::stringstream ss;
ss << "(module ";
ss << "(export \"apply\" (func $apply))";
ss << " (func $apply (param $0 i64) (param $1 i64))";
ss << " (func (param i64) (param f32) ";
for(unsigned int i = 12; i < wasm_constraints::maximum_func_local_bytes+4; i+=4)
ss << "(local f32)";
......@@ -1065,4 +1078,17 @@ BOOST_FIXTURE_TEST_CASE( lotso_stack, tester ) try {
} 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()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册