From 3a66f89ab866b027ddf35872cd73ff1fd1d11f6d Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Fri, 28 Jun 2019 16:48:33 -0400 Subject: [PATCH 1/5] unittests covering wasms with initial memory size of 0 --- unittests/contracts/test_wasts.hpp | 52 ++++++++++++++++++++++++++++++ unittests/wasm_tests.cpp | 46 ++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/unittests/contracts/test_wasts.hpp b/unittests/contracts/test_wasts.hpp index 763d6758acb..1301224fc13 100644 --- a/unittests/contracts/test_wasts.hpp +++ b/unittests/contracts/test_wasts.hpp @@ -876,3 +876,55 @@ static const std::vector varuint_memory_flags{ 0x07, 0x09, 0x01, 0x05, 'a', 'p', 'p', 'l', 'y', 0x00, 0x00, // exports 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b // code }; + +static const char zero_memory_do_nothing[] = R"=====( +(module + (export "apply" (func $apply)) + (memory $0 0) + (func $apply (param i64) (param i64) (param i64) + ) +) +)====="; + +static const char zero_memory_load[] = R"=====( +(module + (export "apply" (func $apply)) + (memory $0 0) + (func $apply (param i64) (param i64) (param i64) + (drop (i64.load (i32.const 0))) + ) +) +)====="; + +static const char zero_memory_intrinsic[] = R"=====( +(module + (export "apply" (func $apply)) + (import "env" "read_transaction" (func $read_transaction (param i32 i32) (result i32))) + (memory $0 0) + (func $apply (param i64) (param i64) (param i64) + (drop (call $read_transaction (i32.const 16) (i32.const 0))) + ) +) +)====="; + +static const char zero_memory_grow[] = R"=====( +(module + (export "apply" (func $apply)) + (memory $0 0) + (func $apply (param i64) (param i64) (param i64) + (drop (grow_memory (i32.const 1))) + (drop (i64.load (i32.const 0))) + ) +) +)====="; + +static const char zero_memory_grow_hi[] = R"=====( +(module + (export "apply" (func $apply)) + (memory $0 0) + (func $apply (param i64) (param i64) (param i64) + (drop (grow_memory (i32.const 1))) + (drop (i64.load (i32.const 70000))) + ) +) +)====="; diff --git a/unittests/wasm_tests.cpp b/unittests/wasm_tests.cpp index c9641da2bee..d669980c606 100644 --- a/unittests/wasm_tests.cpp +++ b/unittests/wasm_tests.cpp @@ -2068,7 +2068,53 @@ BOOST_AUTO_TEST_CASE( billed_cpu_test ) try { } FC_LOG_AND_RETHROW() +/** + * various tests with wasm & 0 pages worth of memory + */ +BOOST_FIXTURE_TEST_CASE( zero_memory_pages, TESTER ) try { + produce_blocks(2); + + create_accounts( {"zero"_n} ); + produce_block(); + + signed_transaction trx; + trx.actions.emplace_back(vector{{"zero"_n,config::active_name}}, "zero"_n, name(), bytes{}); + trx.actions[0].authorization = vector{{"zero"_n,config::active_name}}; + + auto pushit = [&]() { + produce_block(); + trx.signatures.clear(); + set_transaction_headers(trx); + trx.sign(get_private_key("zero"_n, "active"), control->get_chain_id()); + push_transaction(trx); + }; + //first, let's run another large memory contract just to prime the pump so to catch any + //memory reinit faults. + set_code("zero"_n, misaligned_ref_wast); + pushit(); + + //contract w/ 0 pages that does nothing + set_code("zero"_n, zero_memory_do_nothing); + pushit(); + + //memory load with 0 pages of memory + set_code("zero"_n, zero_memory_load); + BOOST_CHECK_THROW(pushit(), wasm_execution_error); + + //do an intrinsic with 0 pages of memory + set_code("zero"_n, zero_memory_intrinsic); + BOOST_CHECK_THROW(pushit(), wasm_execution_error); + + //grow memory from 0 -> 1, should be able to access byte 0 now + set_code("zero"_n, zero_memory_grow); + pushit(); + + //grow memory from 0 -> 1, should be unable to access byte 70K + set_code("zero"_n, zero_memory_grow_hi); + BOOST_CHECK_THROW(pushit(), wasm_execution_error); + +} FC_LOG_AND_RETHROW() // TODO: restore net_usage_tests #if 0 From 74768ff0c94b8c610ffb0aec2fad36ef387aff2b Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Mon, 26 Aug 2019 19:38:07 -0400 Subject: [PATCH 2/5] unittest that calls eosio_exit from start func --- unittests/contracts/test_wasts.hpp | 15 +++++++++++++++ unittests/wasm_tests.cpp | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/unittests/contracts/test_wasts.hpp b/unittests/contracts/test_wasts.hpp index 1301224fc13..91b5c69289a 100644 --- a/unittests/contracts/test_wasts.hpp +++ b/unittests/contracts/test_wasts.hpp @@ -928,3 +928,18 @@ static const char zero_memory_grow_hi[] = R"=====( ) ) )====="; + +static const char exit_in_start_wast[] = R"=====( +(module + (import "env" "abort" (func $abort)) + (import "env" "eosio_exit" (func $eosio_exit (param i32))) + (export "apply" (func $apply)) + (start $dothedew) + (func $dothedew + (call $eosio_exit (i32.const 0)) + ) + (func $apply (param $0 i64) (param $1 i64) (param $2 i64) + (call $abort) + ) +) +)====="; \ No newline at end of file diff --git a/unittests/wasm_tests.cpp b/unittests/wasm_tests.cpp index d669980c606..98d252d5256 100644 --- a/unittests/wasm_tests.cpp +++ b/unittests/wasm_tests.cpp @@ -2116,6 +2116,26 @@ BOOST_FIXTURE_TEST_CASE( zero_memory_pages, TESTER ) try { } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE( eosio_exit_in_start, TESTER ) try { + produce_blocks(2); + create_accounts( {"startexit"_n} ); + produce_block(); + + set_code("startexit"_n, exit_in_start_wast); + produce_blocks(1); + + signed_transaction trx; + action act; + act.account = "startexit"_n; + act.name = name(); + act.authorization = vector{{"startexit"_n,config::active_name}}; + trx.actions.push_back(act); + set_transaction_headers(trx); + trx.sign(get_private_key( "startexit"_n, "active" ), control->get_chain_id()); + push_transaction(trx); + produce_blocks(1); +} FC_LOG_AND_RETHROW() + // TODO: restore net_usage_tests #if 0 BOOST_FIXTURE_TEST_CASE(net_usage_tests, tester ) try { From aa1f685295ff8ace30009a77af3644ed760912f6 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 17 Sep 2019 16:28:35 -0400 Subject: [PATCH 3/5] unit test for grow_memory with a negative argument. --- unittests/contracts/test_wasts.hpp | 15 +++++++++++++++ unittests/wasm_tests.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/unittests/contracts/test_wasts.hpp b/unittests/contracts/test_wasts.hpp index 91b5c69289a..cf6edde757e 100644 --- a/unittests/contracts/test_wasts.hpp +++ b/unittests/contracts/test_wasts.hpp @@ -942,4 +942,19 @@ static const char exit_in_start_wast[] = R"=====( (call $abort) ) ) +)====="; + +static const char negative_memory_grow_wast[] = R"=====( +(module + (memory 1) + (func (;0;) (param i64 i64 i64) + (drop (grow_memory (i32.const 1))) + (grow_memory (i32.const -1)) + (i32.const -1) + (i32.ne) + (br_if 0) + (unreachable) + ) + (export "apply" (func 0)) +) )====="; \ No newline at end of file diff --git a/unittests/wasm_tests.cpp b/unittests/wasm_tests.cpp index 98d252d5256..b9b010e69df 100644 --- a/unittests/wasm_tests.cpp +++ b/unittests/wasm_tests.cpp @@ -2136,6 +2136,31 @@ BOOST_FIXTURE_TEST_CASE( eosio_exit_in_start, TESTER ) try { produce_blocks(1); } FC_LOG_AND_RETHROW() +// memory.grow with a negative argument can shrink the available memory. +BOOST_FIXTURE_TEST_CASE( negative_memory_grow, TESTER ) try { + produce_blocks(2); + + + create_accounts( {"negmemgrow"_n} ); + produce_block(); + + set_code("negmemgrow"_n, negative_memory_grow_wast); + produce_blocks(1); + + signed_transaction trx; + action act; + act.account = "negmemgrow"_n; + act.name = name(); + act.authorization = vector{{"negmemgrow"_n,config::active_name}}; + trx.actions.push_back(act); + + set_transaction_headers(trx); + trx.sign(get_private_key( "negmemgrow"_n, "active" ), control->get_chain_id()); + push_transaction(trx); + produce_blocks(1); + +} FC_LOG_AND_RETHROW() + // TODO: restore net_usage_tests #if 0 BOOST_FIXTURE_TEST_CASE(net_usage_tests, tester ) try { From 21235448758bab10d08dbafe77fdde9f11571843 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Thu, 19 Sep 2019 16:05:10 -0400 Subject: [PATCH 4/5] Add more tests to address review comments. --- unittests/contracts/test_wasts.hpp | 51 ++++++++++++++++++++++++------ unittests/wasm_tests.cpp | 17 ++++++++++ 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/unittests/contracts/test_wasts.hpp b/unittests/contracts/test_wasts.hpp index cf6edde757e..54e64a1b695 100644 --- a/unittests/contracts/test_wasts.hpp +++ b/unittests/contracts/test_wasts.hpp @@ -947,14 +947,47 @@ static const char exit_in_start_wast[] = R"=====( static const char negative_memory_grow_wast[] = R"=====( (module (memory 1) - (func (;0;) (param i64 i64 i64) - (drop (grow_memory (i32.const 1))) - (grow_memory (i32.const -1)) - (i32.const -1) - (i32.ne) - (br_if 0) - (unreachable) - ) - (export "apply" (func 0)) + (func (export "apply") (param i64 i64 i64) + (block + (drop (grow_memory (i32.const 1))) + (i32.eq (grow_memory (i32.const -1)) (i32.const 2)) + (br_if 0) + (unreachable) + ) + (block + (drop (grow_memory (i32.const 2))) + (i32.eq (grow_memory (i32.const -3)) (i32.const 3)) + (br_if 0) + (unreachable) + ) + (block + (drop (grow_memory (i32.const 1))) + (i32.store (i32.const 0) (i32.const -1)) + (i32.store (i32.const 65532) (i32.const -1)) + (grow_memory (i32.const -1)) + (grow_memory (i32.const 1)) + (i32.and (i32.eq (i32.load (i32.const 0)) (i32.const 0)) + (i32.eq (i32.load (i32.const 65532)) (i32.const 0))) + (br_if 0) + (unreachable) + ) + (block + (i32.eq (grow_memory (i32.const -2)) (i32.const -1)) + (br_if 0) + (unreachable) + ) + ) +) +)====="; + +static const char negative_memory_grow_trap_wast[] = R"=====( +(module + (memory 1) + (func (export "apply") (param i64 i64 i64) + (block + (drop (grow_memory (i32.const -1))) + (drop (i32.load (i32.const 0))) + ) + ) ) )====="; \ No newline at end of file diff --git a/unittests/wasm_tests.cpp b/unittests/wasm_tests.cpp index b9b010e69df..420c583ea74 100644 --- a/unittests/wasm_tests.cpp +++ b/unittests/wasm_tests.cpp @@ -2147,6 +2147,7 @@ BOOST_FIXTURE_TEST_CASE( negative_memory_grow, TESTER ) try { set_code("negmemgrow"_n, negative_memory_grow_wast); produce_blocks(1); + { signed_transaction trx; action act; act.account = "negmemgrow"_n; @@ -2158,6 +2159,22 @@ BOOST_FIXTURE_TEST_CASE( negative_memory_grow, TESTER ) try { trx.sign(get_private_key( "negmemgrow"_n, "active" ), control->get_chain_id()); push_transaction(trx); produce_blocks(1); + } + + set_code("negmemgrow"_n, negative_memory_grow_trap_wast); + produce_block(); + { + signed_transaction trx; + action act; + act.account = "negmemgrow"_n; + act.name = name(); + act.authorization = vector{{"negmemgrow"_n,config::active_name}}; + trx.actions.push_back(act); + + set_transaction_headers(trx); + trx.sign(get_private_key( "negmemgrow"_n, "active" ), control->get_chain_id()); + BOOST_CHECK_THROW(push_transaction(trx), eosio::chain::wasm_execution_error); + } } FC_LOG_AND_RETHROW() From 66d5f48289640393201713683ebd08a8089507f6 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 16 Sep 2020 14:35:45 -0400 Subject: [PATCH 5/5] Unit test for incorrect bounds checking on empty null-terminated strings in OC. --- unittests/api_tests.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 766a605fb4b..965c738198a 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -2483,6 +2483,31 @@ BOOST_FIXTURE_TEST_CASE(memory_tests, TESTER) { pushit("memset"_n, name()); } +static const char cstr_wast[] = R"======( +(module + (import "env" "eosio_assert" (func $eosio_assert (param i32 i32))) + (memory 1) + (func (export "apply") (param i64 i64 i64) + (call $eosio_assert (i32.const 1) (i32.const 65534)) + ) + (data (i32.const 65535) "x") +) +)======"; + +BOOST_FIXTURE_TEST_CASE(cstr_tests, TESTER) { + produce_block(); + create_accounts( { "cstr"_n } ); + set_code( "cstr"_n, cstr_wast ); + auto pushit = [&](name acct, name act) { + signed_transaction trx; + trx.actions.push_back({ { {acct, config::active_name} }, acct, act, bytes()}); + set_transaction_headers(trx); + trx.sign(get_private_key(acct, "active"), control->get_chain_id()); + push_transaction(trx); + }; + pushit("cstr"_n, name()); +} + /************************************************************************************* * print_tests test case *************************************************************************************/