From b6fd7e16ef49c7ec33b0a5560c544251545cc6c5 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 31 Oct 2023 13:26:35 -0400 Subject: [PATCH 1/5] disable eosvmoc subjective limits in unit tests --- .../webassembly/eos-vm-oc/code_cache.hpp | 1 + .../chain/webassembly/eos-vm-oc/config.hpp | 12 ++++++++++ .../webassembly/eos-vm-oc/ipc_protocol.hpp | 3 ++- .../runtimes/eos-vm-oc/LLVMJIT.cpp | 10 ++++++--- .../webassembly/runtimes/eos-vm-oc/LLVMJIT.h | 2 +- .../runtimes/eos-vm-oc/code_cache.cpp | 7 +++--- .../runtimes/eos-vm-oc/compile_monitor.cpp | 6 ++--- .../runtimes/eos-vm-oc/compile_trampoline.cpp | 22 +++++++++++++------ libraries/testing/tester.cpp | 11 ++++++++++ 9 files changed, 56 insertions(+), 18 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp index d753a9dcaa..f638dc8751 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/code_cache.hpp @@ -69,6 +69,7 @@ class code_cache_base { code_cache_index _cache_index; const chainbase::database& _db; + eosvmoc::config _eosvmoc_config; std::filesystem::path _cache_file_path; int _cache_fd; diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp index b3f2563831..1de0293bd0 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp @@ -7,11 +7,23 @@ #include +#include + namespace eosio { namespace chain { namespace eosvmoc { struct config { uint64_t cache_size = 1024u*1024u*1024u; uint64_t threads = 1u; + + // subjective limits. + // nodeos uses the default values. libtester sets them as required. + rlimit cpu_limits {20u, 20u}; + rlimit vm_limits {512u*1024u*1024u, 512u*1024u*1024u}; + uint64_t stack_size_limit {16u*1024u}; + size_t generated_code_size_limit {16u*1024u*1024u}; }; }}} + +FC_REFLECT(rlimit, (rlim_cur)(rlim_max)) +FC_REFLECT(eosio::chain::eosvmoc::config, (cache_size)(threads)(cpu_limits)(vm_limits)(stack_size_limit)(generated_code_size_limit)) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/ipc_protocol.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/ipc_protocol.hpp index 2674a2dbd4..59fb7f10fb 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/ipc_protocol.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/ipc_protocol.hpp @@ -22,6 +22,7 @@ struct code_tuple { struct compile_wasm_message { code_tuple code; + eosvmoc::config eosvmoc_config; //Two sent fd: 1) communication socket for result, 2) the wasm to compile }; @@ -62,7 +63,7 @@ using eosvmoc_message = std::variantcode); } - instantiated_code instantiateModule(const IR::Module& module) + instantiated_code instantiateModule(const IR::Module& module, uint64_t stack_size_limit, size_t generated_code_size_limit) { static bool inited; if(!inited) { @@ -315,13 +315,17 @@ namespace LLVMJIT WAVM_ASSERT_THROW(!!c); ++num_functions_stack_size_found; - if(stack_size > 16u*1024u) + // enforce stack_size_limit only when it is not max (libtester may + // disable it by setting it to max). + if(stack_size_limit != std::numeric_limits::max() && stack_size > stack_size_limit) _exit(1); } } if(num_functions_stack_size_found != module.functions.defs.size()) _exit(1); - if(jitModule->final_pic_code.size() >= 16u*1024u*1024u) + // enforce generated_code_size_limit only when it is not max (libtester may + // disable it by setting it to max). + if(generated_code_size_limit != std::numeric_limits::max() && jitModule->final_pic_code.size() >= generated_code_size_limit) _exit(1); instantiated_code ret; diff --git a/libraries/chain/webassembly/runtimes/eos-vm-oc/LLVMJIT.h b/libraries/chain/webassembly/runtimes/eos-vm-oc/LLVMJIT.h index 13e2510195..0cd022ce3b 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm-oc/LLVMJIT.h +++ b/libraries/chain/webassembly/runtimes/eos-vm-oc/LLVMJIT.h @@ -15,6 +15,6 @@ struct instantiated_code { }; namespace LLVMJIT { - instantiated_code instantiateModule(const IR::Module& module); + instantiated_code instantiateModule(const IR::Module& module, uint64_t stack_size_limit, size_t generated_code_size_limit); } }}} diff --git a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp index 7c48b5b079..e4a23686e8 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm-oc/code_cache.cpp @@ -126,7 +126,7 @@ const code_descriptor* const code_cache_async::get_descriptor_for_code(bool high _outstanding_compiles_and_poison.emplace(*nextup, false); std::vector fds_to_pass; fds_to_pass.emplace_back(memfd_for_bytearray(codeobject->code)); - FC_ASSERT(write_message_with_fds(_compile_monitor_write_socket, compile_wasm_message{ *nextup }, fds_to_pass), "EOS VM failed to communicate to OOP manager"); + FC_ASSERT(write_message_with_fds(_compile_monitor_write_socket, compile_wasm_message{ *nextup, _eosvmoc_config }, fds_to_pass), "EOS VM failed to communicate to OOP manager"); --count_processed; } _queued_compiles.erase(nextup); @@ -179,7 +179,7 @@ const code_descriptor* const code_cache_async::get_descriptor_for_code(bool high _outstanding_compiles_and_poison.emplace(ct, false); std::vector fds_to_pass; fds_to_pass.emplace_back(memfd_for_bytearray(codeobject->code)); - write_message_with_fds(_compile_monitor_write_socket, compile_wasm_message{ ct }, fds_to_pass); + write_message_with_fds(_compile_monitor_write_socket, compile_wasm_message{ ct, _eosvmoc_config }, fds_to_pass); failure = get_cd_failure::temporary; // Compile might not be done yet return nullptr; } @@ -211,7 +211,7 @@ const code_descriptor* const code_cache_sync::get_descriptor_for_code_sync(const std::vector fds_to_pass; fds_to_pass.emplace_back(memfd_for_bytearray(codeobject->code)); - write_message_with_fds(_compile_monitor_write_socket, compile_wasm_message{ {code_id, vm_version} }, fds_to_pass); + write_message_with_fds(_compile_monitor_write_socket, compile_wasm_message{ {code_id, vm_version}, _eosvmoc_config }, fds_to_pass); auto [success, message, fds] = read_message_with_fds(_compile_monitor_read_socket); EOS_ASSERT(success, wasm_execution_error, "failed to read response from monitor process"); EOS_ASSERT(std::holds_alternative(message), wasm_execution_error, "unexpected response from monitor process"); @@ -226,6 +226,7 @@ const code_descriptor* const code_cache_sync::get_descriptor_for_code_sync(const code_cache_base::code_cache_base(const std::filesystem::path& data_dir, const eosvmoc::config& eosvmoc_config, const chainbase::database& db) : _db(db), + _eosvmoc_config(eosvmoc_config), _cache_file_path(data_dir/"code_cache.bin") { static_assert(sizeof(allocator_t) <= header_offset, "header offset intersects with allocator"); diff --git a/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_monitor.cpp b/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_monitor.cpp index 01ae7ecaba..92b55da717 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_monitor.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_monitor.cpp @@ -71,7 +71,7 @@ struct compile_monitor_session { connection_dead_signal(); return; } - kick_compile_off(compile.code, std::move(fds[0])); + kick_compile_off(compile.code, compile.eosvmoc_config, std::move(fds[0])); }, [&](const evict_wasms_message& evict) { for(const code_descriptor& cd : evict.codes) { @@ -90,7 +90,7 @@ struct compile_monitor_session { }); } - void kick_compile_off(const code_tuple& code_id, wrapped_fd&& wasm_code) { + void kick_compile_off(const code_tuple& code_id, const eosvmoc::config& eosvmoc_config, wrapped_fd&& wasm_code) { //prepare a requst to go out to the trampoline int socks[2]; socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, socks); @@ -100,7 +100,7 @@ struct compile_monitor_session { fds_pass_to_trampoline.emplace_back(socks[1]); fds_pass_to_trampoline.emplace_back(std::move(wasm_code)); - eosvmoc_message trampoline_compile_request = compile_wasm_message{code_id}; + eosvmoc_message trampoline_compile_request = compile_wasm_message{code_id, eosvmoc_config}; if(write_message_with_fds(_trampoline_socket, trampoline_compile_request, fds_pass_to_trampoline) == false) { wasm_compilation_result_message reply{code_id, compilation_result_unknownfailure{}, _allocator->get_free_memory()}; write_message_with_fds(_nodeos_instance_socket, reply); diff --git a/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_trampoline.cpp b/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_trampoline.cpp index b2f16a6d87..de506119f2 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_trampoline.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_trampoline.cpp @@ -17,7 +17,7 @@ using namespace IR; namespace eosio { namespace chain { namespace eosvmoc { -void run_compile(wrapped_fd&& response_sock, wrapped_fd&& wasm_code) noexcept { //noexcept; we'll just blow up if anything tries to cross this boundry +void run_compile(wrapped_fd&& response_sock, wrapped_fd&& wasm_code, uint64_t stack_size_limit, size_t generated_code_size_limit) noexcept { //noexcept; we'll just blow up if anything tries to cross this boundry std::vector wasm = vector_for_memfd(wasm_code); //ideally we catch exceptions and sent them upstream as strings for easier reporting @@ -30,7 +30,7 @@ void run_compile(wrapped_fd&& response_sock, wrapped_fd&& wasm_code) noexcept { wasm_injections::wasm_binary_injection injector(module); injector.inject(); - instantiated_code code = LLVMJIT::instantiateModule(module); + instantiated_code code = LLVMJIT::instantiateModule(module, stack_size_limit, generated_code_size_limit); code_compilation_result_message result_message; @@ -165,16 +165,24 @@ void run_compile_trampoline(int fd) { prctl(PR_SET_NAME, "oc-compile"); prctl(PR_SET_PDEATHSIG, SIGKILL); - struct rlimit cpu_limits = {20u, 20u}; - setrlimit(RLIMIT_CPU, &cpu_limits); + const auto& conf = std::get(message).eosvmoc_config; - struct rlimit vm_limits = {512u*1024u*1024u, 512u*1024u*1024u}; - setrlimit(RLIMIT_AS, &vm_limits); + // enforce cpu_limits only when it is not RLIM_INFINITY (libtester may + // disable it by setting it to RLIM_INFINITY). + if(conf.cpu_limits.rlim_cur != RLIM_INFINITY) { + setrlimit(RLIMIT_CPU, &conf.cpu_limits); + } + + // enforce vm_limits only when it is not RLIM_INFINITY (libtester may + // disable it by setting it to RLIM_INFINITY). + if(conf.vm_limits.rlim_cur != RLIM_INFINITY) { + setrlimit(RLIMIT_AS, &conf.vm_limits); + } struct rlimit core_limits = {0u, 0u}; setrlimit(RLIMIT_CORE, &core_limits); - run_compile(std::move(fds[0]), std::move(fds[1])); + run_compile(std::move(fds[0]), std::move(fds[1]), conf.stack_size_limit, conf.generated_code_size_limit); _exit(0); } else if(pid == -1) diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index 8a9b7a0ad6..2db0b28f11 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -317,6 +317,17 @@ namespace eosio { namespace testing { } } + // libtester does not enforce EOSVMOC limits for all tests except those in + // eosvmoc_limits_test.cpp, where one or more of the limits are set to + // max to indicate the limits are enforced. + if (cfg.eosvmoc_config.cpu_limits.rlim_cur != RLIM_INFINITY && cfg.eosvmoc_config.vm_limits.rlim_cur != RLIM_INFINITY && cfg.eosvmoc_config.stack_size_limit != std::numeric_limits::max() && cfg.eosvmoc_config.generated_code_size_limit != std::numeric_limits::max()) { + // set to inifinity or max such that they are not enforced. + cfg.eosvmoc_config.cpu_limits.rlim_cur = RLIM_INFINITY; + cfg.eosvmoc_config.vm_limits.rlim_cur = RLIM_INFINITY; + cfg.eosvmoc_config.stack_size_limit = std::numeric_limits::max(); + cfg.eosvmoc_config.generated_code_size_limit = std::numeric_limits::max(); + } + control.reset( new controller(cfg, std::move(pfs), *expected_chain_id) ); control->add_indices(); if (lambda) lambda(); From c012b146713eafa5a038df854aca4ea79c7258e3 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 31 Oct 2023 13:27:20 -0400 Subject: [PATCH 2/5] add eosvmoc limits tests --- unittests/eosvmoc_limits_tests.cpp | 139 +++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 unittests/eosvmoc_limits_tests.cpp diff --git a/unittests/eosvmoc_limits_tests.cpp b/unittests/eosvmoc_limits_tests.cpp new file mode 100644 index 0000000000..a0d096cead --- /dev/null +++ b/unittests/eosvmoc_limits_tests.cpp @@ -0,0 +1,139 @@ +#ifdef EOSIO_EOS_VM_OC_RUNTIME_ENABLED + +#include +#include +#include + +using namespace eosio; +using namespace eosio::chain; +using namespace eosio::testing; +using mvo = fc::mutable_variant_object; + +BOOST_AUTO_TEST_SUITE(eosvmoc_limits_tests) + +// common routine to verify wasm_execution_error is raised when a resource +// limit specified in eosvmoc_config is reached +void limit_violated_test(const eosvmoc::config eosvmoc_config) { + fc::temp_directory tempdir; + + constexpr bool use_genesis = true; + validating_tester chain( + tempdir, + [&](controller::config& cfg) { + cfg.eosvmoc_config = eosvmoc_config; + }, + use_genesis + ); + + chain.create_accounts({"eosio.token"_n}); + chain.set_code("eosio.token"_n, test_contracts::eosio_token_wasm()); + chain.set_abi("eosio.token"_n, test_contracts::eosio_token_abi()); + + if (chain.control->is_eos_vm_oc_enabled()) { + BOOST_CHECK_EXCEPTION( + chain.push_action( "eosio.token"_n, "create"_n, "eosio.token"_n, mvo() + ( "issuer", "eosio.token" ) + ( "maximum_supply", "1000000.00 TOK" )), + eosio::chain::wasm_execution_error, + [](const eosio::chain::wasm_execution_error& e) { + return expect_assert_message(e, "failed to compile wasm"); + } + ); + } else { + chain.push_action( "eosio.token"_n, "create"_n, "eosio.token"_n, mvo() + ( "issuer", "eosio.token" ) + ( "maximum_supply", "1000000.00 TOK" ) + ); + } +} + +// common routine to verify no wasm_execution_error is raised +// because limits specified in eosvmoc_config are not reached +void limit_not_violated_test(const eosvmoc::config eosvmoc_config) { + fc::temp_directory tempdir; + + constexpr bool use_genesis = true; + validating_tester chain( + tempdir, + [&](controller::config& cfg) { + cfg.eosvmoc_config = eosvmoc_config; + }, + use_genesis + ); + + chain.create_accounts({"eosio.token"_n}); + chain.set_code("eosio.token"_n, test_contracts::eosio_token_wasm()); + chain.set_abi("eosio.token"_n, test_contracts::eosio_token_abi()); + + chain.push_action( "eosio.token"_n, "create"_n, "eosio.token"_n, mvo() + ( "issuer", "eosio.token" ) + ( "maximum_supply", "1000000.00 TOK" ) + ); +} + +// test libtester does not enforce limits +BOOST_AUTO_TEST_CASE( default_limits ) { try { + eosvmoc::config eosvmoc_config; + limit_not_violated_test(eosvmoc_config); +} FC_LOG_AND_RETHROW() } + +// test VM limits are checked +BOOST_AUTO_TEST_CASE( vm_limits ) { try { + eosvmoc::config eosvmoc_config; + + // disable non-tested limits + eosvmoc_config.cpu_limits.rlim_cur = RLIM_INFINITY; + eosvmoc_config.stack_size_limit = std::numeric_limits::max(); + eosvmoc_config.generated_code_size_limit = std::numeric_limits::max(); + + // set vm_limit to a small value such that it is exceeded + eosvmoc_config.vm_limits.rlim_cur = 64u*1024u*1024u; + limit_violated_test(eosvmoc_config); + + // set vm_limit to a large value such that it is not exceeded + eosvmoc_config.vm_limits.rlim_cur = 128u*1024u*1024u; + limit_not_violated_test(eosvmoc_config); +} FC_LOG_AND_RETHROW() } + +// test stack size limit is checked +BOOST_AUTO_TEST_CASE( stack_limit ) { try { + eosvmoc::config eosvmoc_config; + + // disable non-tested limits + eosvmoc_config.cpu_limits.rlim_cur = RLIM_INFINITY; + eosvmoc_config.vm_limits.rlim_cur = RLIM_INFINITY; + eosvmoc_config.generated_code_size_limit = std::numeric_limits::max(); + + // The larget stack size in the test is 104. + // Set stack_size_limit to highest value to verify wasm_execution_error is raised + eosvmoc_config.stack_size_limit = 103; + limit_violated_test(eosvmoc_config); + + // set stack_size_limit to lowest value to verify wasm_execution_error is not raised + eosvmoc_config.stack_size_limit = 104; + limit_not_violated_test(eosvmoc_config); +} FC_LOG_AND_RETHROW() } + +// test generated code size limit is checked +BOOST_AUTO_TEST_CASE( generated_code_size_limit ) { try { + eosvmoc::config eosvmoc_config; + + // disable non-tested limits + eosvmoc_config.cpu_limits.rlim_cur = RLIM_INFINITY; + eosvmoc_config.vm_limits.rlim_cur = RLIM_INFINITY; + eosvmoc_config.stack_size_limit = std::numeric_limits::max(); + + // The sgenerated code size in the test is 36856. + // Set generated_code_size_limit to highest value to verify wasm_execution_error + // is raised + eosvmoc_config.generated_code_size_limit = 36856; + limit_violated_test(eosvmoc_config); + + // set generated_code_size_limit to lowest value to verify wasm_execution_error is not raised + eosvmoc_config.generated_code_size_limit = 36857; + limit_not_violated_test(eosvmoc_config); +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_SUITE_END() + +#endif From e4df534c675e8133b257a783cde4326c76618c93 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Wed, 1 Nov 2023 19:51:47 -0400 Subject: [PATCH 3/5] incorporate review comments --- .../chain/webassembly/eos-vm-oc/config.hpp | 6 ++++-- .../webassembly/runtimes/eos-vm-oc/LLVMJIT.cpp | 8 ++------ unittests/eosvmoc_limits_tests.cpp | 17 ++++++++--------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp index 1de0293bd0..fcda6a368d 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp @@ -15,8 +15,10 @@ struct config { uint64_t cache_size = 1024u*1024u*1024u; uint64_t threads = 1u; - // subjective limits. - // nodeos uses the default values. libtester sets them as required. + // subjective limits for OC compilation. + // nodeos enforces the limits by the default values. + // libtester disables the limits in all tests, except enforces the limits + // in the tests in unittests/eosvmoc_limits_tests.cpp. rlimit cpu_limits {20u, 20u}; rlimit vm_limits {512u*1024u*1024u, 512u*1024u*1024u}; uint64_t stack_size_limit {16u*1024u}; diff --git a/libraries/chain/webassembly/runtimes/eos-vm-oc/LLVMJIT.cpp b/libraries/chain/webassembly/runtimes/eos-vm-oc/LLVMJIT.cpp index b0b9645b6e..acac1f3582 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm-oc/LLVMJIT.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm-oc/LLVMJIT.cpp @@ -315,17 +315,13 @@ namespace LLVMJIT WAVM_ASSERT_THROW(!!c); ++num_functions_stack_size_found; - // enforce stack_size_limit only when it is not max (libtester may - // disable it by setting it to max). - if(stack_size_limit != std::numeric_limits::max() && stack_size > stack_size_limit) + if(stack_size > stack_size_limit) _exit(1); } } if(num_functions_stack_size_found != module.functions.defs.size()) _exit(1); - // enforce generated_code_size_limit only when it is not max (libtester may - // disable it by setting it to max). - if(generated_code_size_limit != std::numeric_limits::max() && jitModule->final_pic_code.size() >= generated_code_size_limit) + if(jitModule->final_pic_code.size() >= generated_code_size_limit) _exit(1); instantiated_code ret; diff --git a/unittests/eosvmoc_limits_tests.cpp b/unittests/eosvmoc_limits_tests.cpp index a0d096cead..64c4007dc3 100644 --- a/unittests/eosvmoc_limits_tests.cpp +++ b/unittests/eosvmoc_limits_tests.cpp @@ -13,7 +13,7 @@ BOOST_AUTO_TEST_SUITE(eosvmoc_limits_tests) // common routine to verify wasm_execution_error is raised when a resource // limit specified in eosvmoc_config is reached -void limit_violated_test(const eosvmoc::config eosvmoc_config) { +void limit_violated_test(const eosvmoc::config& eosvmoc_config) { fc::temp_directory tempdir; constexpr bool use_genesis = true; @@ -49,7 +49,7 @@ void limit_violated_test(const eosvmoc::config eosvmoc_config) { // common routine to verify no wasm_execution_error is raised // because limits specified in eosvmoc_config are not reached -void limit_not_violated_test(const eosvmoc::config eosvmoc_config) { +void limit_not_violated_test(const eosvmoc::config& eosvmoc_config) { fc::temp_directory tempdir; constexpr bool use_genesis = true; @@ -104,12 +104,12 @@ BOOST_AUTO_TEST_CASE( stack_limit ) { try { eosvmoc_config.vm_limits.rlim_cur = RLIM_INFINITY; eosvmoc_config.generated_code_size_limit = std::numeric_limits::max(); - // The larget stack size in the test is 104. - // Set stack_size_limit to highest value to verify wasm_execution_error is raised + // The stack size of the compiled WASM in the test is 104. + // Set stack_size_limit one less than the actual needed stack size eosvmoc_config.stack_size_limit = 103; limit_violated_test(eosvmoc_config); - // set stack_size_limit to lowest value to verify wasm_execution_error is not raised + // set stack_size_limit to the actual needed stack size eosvmoc_config.stack_size_limit = 104; limit_not_violated_test(eosvmoc_config); } FC_LOG_AND_RETHROW() } @@ -123,13 +123,12 @@ BOOST_AUTO_TEST_CASE( generated_code_size_limit ) { try { eosvmoc_config.vm_limits.rlim_cur = RLIM_INFINITY; eosvmoc_config.stack_size_limit = std::numeric_limits::max(); - // The sgenerated code size in the test is 36856. - // Set generated_code_size_limit to highest value to verify wasm_execution_error - // is raised + // The generated code size of the compiled WASM in the test is 36856. + // Set generated_code_size_limit to the actual generated code size eosvmoc_config.generated_code_size_limit = 36856; limit_violated_test(eosvmoc_config); - // set generated_code_size_limit to lowest value to verify wasm_execution_error is not raised + // Set generated_code_size_limit to one above the actual generated code size eosvmoc_config.generated_code_size_limit = 36857; limit_not_violated_test(eosvmoc_config); } FC_LOG_AND_RETHROW() } From 1b0cebae0a7cb9c99a905b4d84de02c8bc20bd3e Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 7 Nov 2023 13:59:13 -0500 Subject: [PATCH 4/5] use tester's default_config, use std::optional for limits. remove hard/soft cpu and vm limits --- .../chain/webassembly/eos-vm-oc/config.hpp | 10 ++--- .../runtimes/eos-vm-oc/compile_trampoline.cpp | 22 ++++++----- .../testing/include/eosio/testing/tester.hpp | 7 ++++ libraries/testing/tester.cpp | 11 ------ unittests/eosvmoc_limits_tests.cpp | 39 +++++++++---------- 5 files changed, 43 insertions(+), 46 deletions(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp index fcda6a368d..b8da19ecfb 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp @@ -19,13 +19,13 @@ struct config { // nodeos enforces the limits by the default values. // libtester disables the limits in all tests, except enforces the limits // in the tests in unittests/eosvmoc_limits_tests.cpp. - rlimit cpu_limits {20u, 20u}; - rlimit vm_limits {512u*1024u*1024u, 512u*1024u*1024u}; - uint64_t stack_size_limit {16u*1024u}; - size_t generated_code_size_limit {16u*1024u*1024u}; + std::optional cpu_limit {20u}; + std::optional vm_limit {512u*1024u*1024u}; + std::optional stack_size_limit {16u*1024u}; + std::optional generated_code_size_limit {16u*1024u*1024u}; }; }}} FC_REFLECT(rlimit, (rlim_cur)(rlim_max)) -FC_REFLECT(eosio::chain::eosvmoc::config, (cache_size)(threads)(cpu_limits)(vm_limits)(stack_size_limit)(generated_code_size_limit)) +FC_REFLECT(eosio::chain::eosvmoc::config, (cache_size)(threads)(cpu_limit)(vm_limit)(stack_size_limit)(generated_code_size_limit)) diff --git a/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_trampoline.cpp b/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_trampoline.cpp index de506119f2..11467afd27 100644 --- a/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_trampoline.cpp +++ b/libraries/chain/webassembly/runtimes/eos-vm-oc/compile_trampoline.cpp @@ -167,22 +167,26 @@ void run_compile_trampoline(int fd) { const auto& conf = std::get(message).eosvmoc_config; - // enforce cpu_limits only when it is not RLIM_INFINITY (libtester may - // disable it by setting it to RLIM_INFINITY). - if(conf.cpu_limits.rlim_cur != RLIM_INFINITY) { - setrlimit(RLIMIT_CPU, &conf.cpu_limits); + // enforce cpu limit only when it is set + // (libtester may disable it) + if(conf.cpu_limit) { + struct rlimit cpu_limit = {*conf.cpu_limit, *conf.cpu_limit}; + setrlimit(RLIMIT_CPU, &cpu_limit); } - // enforce vm_limits only when it is not RLIM_INFINITY (libtester may - // disable it by setting it to RLIM_INFINITY). - if(conf.vm_limits.rlim_cur != RLIM_INFINITY) { - setrlimit(RLIMIT_AS, &conf.vm_limits); + // enforce vm limit only when it is set + // (libtester may disable it) + if(conf.vm_limit) { + struct rlimit vm_limit = {*conf.vm_limit, *conf.vm_limit}; + setrlimit(RLIMIT_AS, &vm_limit); } struct rlimit core_limits = {0u, 0u}; setrlimit(RLIMIT_CORE, &core_limits); - run_compile(std::move(fds[0]), std::move(fds[1]), conf.stack_size_limit, conf.generated_code_size_limit); + uint64_t stack_size_limit = conf.stack_size_limit ? *conf.stack_size_limit : std::numeric_limits::max(); + size_t generated_code_size_limit = conf.generated_code_size_limit ? * conf.generated_code_size_limit : std::numeric_limits::max(); + run_compile(std::move(fds[0]), std::move(fds[1]), stack_size_limit, generated_code_size_limit); _exit(0); } else if(pid == -1) diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index 5389dc076c..ef559ad985 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -406,6 +406,13 @@ namespace eosio { namespace testing { cfg.contracts_console = true; cfg.eosvmoc_config.cache_size = 1024*1024*8; + // don't enforce OC compilation subject limits for tests, + // particularly EOS EVM tests may run over those limits + cfg.eosvmoc_config.cpu_limit.reset(); + cfg.eosvmoc_config.vm_limit.reset(); + cfg.eosvmoc_config.stack_size_limit.reset(); + cfg.eosvmoc_config.generated_code_size_limit.reset(); + // don't use auto tier up for tests, since the point is to test diff vms cfg.eosvmoc_tierup = chain::wasm_interface::vm_oc_enable::oc_none; diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index 2db0b28f11..8a9b7a0ad6 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -317,17 +317,6 @@ namespace eosio { namespace testing { } } - // libtester does not enforce EOSVMOC limits for all tests except those in - // eosvmoc_limits_test.cpp, where one or more of the limits are set to - // max to indicate the limits are enforced. - if (cfg.eosvmoc_config.cpu_limits.rlim_cur != RLIM_INFINITY && cfg.eosvmoc_config.vm_limits.rlim_cur != RLIM_INFINITY && cfg.eosvmoc_config.stack_size_limit != std::numeric_limits::max() && cfg.eosvmoc_config.generated_code_size_limit != std::numeric_limits::max()) { - // set to inifinity or max such that they are not enforced. - cfg.eosvmoc_config.cpu_limits.rlim_cur = RLIM_INFINITY; - cfg.eosvmoc_config.vm_limits.rlim_cur = RLIM_INFINITY; - cfg.eosvmoc_config.stack_size_limit = std::numeric_limits::max(); - cfg.eosvmoc_config.generated_code_size_limit = std::numeric_limits::max(); - } - control.reset( new controller(cfg, std::move(pfs), *expected_chain_id) ); control->add_indices(); if (lambda) lambda(); diff --git a/unittests/eosvmoc_limits_tests.cpp b/unittests/eosvmoc_limits_tests.cpp index 64c4007dc3..4bf3e0cff2 100644 --- a/unittests/eosvmoc_limits_tests.cpp +++ b/unittests/eosvmoc_limits_tests.cpp @@ -71,27 +71,34 @@ void limit_not_violated_test(const eosvmoc::config& eosvmoc_config) { ); } -// test libtester does not enforce limits -BOOST_AUTO_TEST_CASE( default_limits ) { try { +// test all limits are not set for tests +BOOST_AUTO_TEST_CASE( limits_not_set ) { try { + validating_tester chain; + auto& cfg = chain.get_config(); + + BOOST_REQUIRE(cfg.eosvmoc_config.cpu_limit == std::nullopt); + BOOST_REQUIRE(cfg.eosvmoc_config.vm_limit == std::nullopt); + BOOST_REQUIRE(cfg.eosvmoc_config.stack_size_limit == std::nullopt); + BOOST_REQUIRE(cfg.eosvmoc_config.generated_code_size_limit == std::nullopt); +} FC_LOG_AND_RETHROW() } + +// test limits are not enforced unless limits in eosvmoc_config +// are modified +BOOST_AUTO_TEST_CASE( limits_not_enforced ) { try { eosvmoc::config eosvmoc_config; limit_not_violated_test(eosvmoc_config); } FC_LOG_AND_RETHROW() } -// test VM limits are checked -BOOST_AUTO_TEST_CASE( vm_limits ) { try { +// test VM limit are checked +BOOST_AUTO_TEST_CASE( vm_limit ) { try { eosvmoc::config eosvmoc_config; - // disable non-tested limits - eosvmoc_config.cpu_limits.rlim_cur = RLIM_INFINITY; - eosvmoc_config.stack_size_limit = std::numeric_limits::max(); - eosvmoc_config.generated_code_size_limit = std::numeric_limits::max(); - // set vm_limit to a small value such that it is exceeded - eosvmoc_config.vm_limits.rlim_cur = 64u*1024u*1024u; + eosvmoc_config.vm_limit = 64u*1024u*1024u; limit_violated_test(eosvmoc_config); // set vm_limit to a large value such that it is not exceeded - eosvmoc_config.vm_limits.rlim_cur = 128u*1024u*1024u; + eosvmoc_config.vm_limit = 128u*1024u*1024u; limit_not_violated_test(eosvmoc_config); } FC_LOG_AND_RETHROW() } @@ -99,11 +106,6 @@ BOOST_AUTO_TEST_CASE( vm_limits ) { try { BOOST_AUTO_TEST_CASE( stack_limit ) { try { eosvmoc::config eosvmoc_config; - // disable non-tested limits - eosvmoc_config.cpu_limits.rlim_cur = RLIM_INFINITY; - eosvmoc_config.vm_limits.rlim_cur = RLIM_INFINITY; - eosvmoc_config.generated_code_size_limit = std::numeric_limits::max(); - // The stack size of the compiled WASM in the test is 104. // Set stack_size_limit one less than the actual needed stack size eosvmoc_config.stack_size_limit = 103; @@ -118,11 +120,6 @@ BOOST_AUTO_TEST_CASE( stack_limit ) { try { BOOST_AUTO_TEST_CASE( generated_code_size_limit ) { try { eosvmoc::config eosvmoc_config; - // disable non-tested limits - eosvmoc_config.cpu_limits.rlim_cur = RLIM_INFINITY; - eosvmoc_config.vm_limits.rlim_cur = RLIM_INFINITY; - eosvmoc_config.stack_size_limit = std::numeric_limits::max(); - // The generated code size of the compiled WASM in the test is 36856. // Set generated_code_size_limit to the actual generated code size eosvmoc_config.generated_code_size_limit = 36856; From 9f94a40f236835b965459ab88b46490d14874afd Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 7 Nov 2023 15:59:44 -0500 Subject: [PATCH 5/5] remove unused FC_REFLECT for rlimit --- .../chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp index b8da19ecfb..43f9efede2 100644 --- a/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp +++ b/libraries/chain/include/eosio/chain/webassembly/eos-vm-oc/config.hpp @@ -27,5 +27,4 @@ struct config { }}} -FC_REFLECT(rlimit, (rlim_cur)(rlim_max)) FC_REFLECT(eosio::chain::eosvmoc::config, (cache_size)(threads)(cpu_limit)(vm_limit)(stack_size_limit)(generated_code_size_limit))