From bc3ca99265047ae336f65498ce82b8cf2d2e9abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 14 Feb 2022 22:06:24 +0100 Subject: [PATCH 1/3] Rework grow_memory() helper - pass gas by value --- lib/evmone/instructions.hpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index 6c764b12dc..ad7dab2c80 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -58,29 +58,29 @@ inline constexpr int64_t num_words(uint64_t size_in_bytes) noexcept return static_cast((size_in_bytes + (word_size - 1)) / word_size); } -// Grows EVM memory and checks its cost. -// -// This function should not be inlined because this may affect other inlining decisions: -// - making check_memory() too costly to inline, -// - making mload()/mstore()/mstore8() too costly to inline. -// -// TODO: This function should be moved to Memory class. -[[gnu::noinline]] inline bool grow_memory(ExecutionState& state, uint64_t new_size) noexcept +/// Grows EVM memory and checks its cost. +/// +/// This function should not be inlined because this may affect other inlining decisions: +/// - making check_memory() too costly to inline, +/// - making mload()/mstore()/mstore8() too costly to inline. +/// +/// TODO: This function should be moved to Memory class. +[[gnu::noinline]] inline int64_t grow_memory( + int64_t gas_left, Memory& memory, uint64_t new_size) noexcept { // This implementation recomputes memory.size(). This value is already known to the caller // and can be passed as a parameter, but this make no difference to the performance. const auto new_words = num_words(new_size); - const auto current_words = static_cast(state.memory.size() / word_size); + const auto current_words = static_cast(memory.size() / word_size); const auto new_cost = 3 * new_words + new_words * new_words / 512; const auto current_cost = 3 * current_words + current_words * current_words / 512; const auto cost = new_cost - current_cost; - if ((state.gas_left -= cost) < 0) - return false; - - state.memory.grow(static_cast(new_words * word_size)); - return true; + gas_left -= cost; + if (gas_left >= 0) [[likely]] + memory.grow(static_cast(new_words * word_size)); + return gas_left; } // Check memory requirements of a reasonable size. @@ -94,9 +94,9 @@ inline bool check_memory(ExecutionState& state, const uint256& offset, uint64_t const auto new_size = static_cast(offset) + size; if (new_size > state.memory.size()) - return grow_memory(state, new_size); + state.gas_left = grow_memory(state.gas_left, state.memory, new_size); - return true; + return state.gas_left >= 0; // Always true for no-grow case. } // Check memory requirements for "copy" instructions. From 24e615ddb1a5e0c6a0f25363df224b879500d714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Mar 2023 19:59:31 +0200 Subject: [PATCH 2/3] Rework check_memory() - pass gas and memory refs --- lib/evmone/instructions.hpp | 38 ++++++++++++++++--------------- lib/evmone/instructions_calls.cpp | 6 ++--- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index ad7dab2c80..8f3ce87d5b 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -83,8 +83,9 @@ inline constexpr int64_t num_words(uint64_t size_in_bytes) noexcept return gas_left; } -// Check memory requirements of a reasonable size. -inline bool check_memory(ExecutionState& state, const uint256& offset, uint64_t size) noexcept +/// Check memory requirements of a reasonable size. +inline bool check_memory( + int64_t& gas_left, Memory& memory, const uint256& offset, uint64_t size) noexcept { // TODO: This should be done in intx. // There is "branchless" variant of this using | instead of ||, but benchmarks difference @@ -93,14 +94,15 @@ inline bool check_memory(ExecutionState& state, const uint256& offset, uint64_t return false; const auto new_size = static_cast(offset) + size; - if (new_size > state.memory.size()) - state.gas_left = grow_memory(state.gas_left, state.memory, new_size); + if (new_size > memory.size()) + gas_left = grow_memory(gas_left, memory, new_size); - return state.gas_left >= 0; // Always true for no-grow case. + return gas_left >= 0; // Always true for no-grow case. } -// Check memory requirements for "copy" instructions. -inline bool check_memory(ExecutionState& state, const uint256& offset, const uint256& size) noexcept +/// Check memory requirements for "copy" instructions. +inline bool check_memory( + int64_t& gas_left, Memory& memory, const uint256& offset, const uint256& size) noexcept { if (size == 0) // Copy of size 0 is always valid (even if offset is huge). return true; @@ -111,7 +113,7 @@ inline bool check_memory(ExecutionState& state, const uint256& offset, const uin if (((size[3] | size[2] | size[1]) != 0) || (size[0] > max_buffer_size)) return false; - return check_memory(state, offset, static_cast(size)); + return check_memory(gas_left, memory, offset, static_cast(size)); } namespace instr::core @@ -337,7 +339,7 @@ inline evmc_status_code keccak256(StackTop stack, ExecutionState& state) noexcep const auto& index = stack.pop(); auto& size = stack.top(); - if (!check_memory(state, index, size)) + if (!check_memory(state.gas_left, state.memory, index, size)) return EVMC_OUT_OF_GAS; const auto i = static_cast(index); @@ -418,7 +420,7 @@ inline evmc_status_code calldatacopy(StackTop stack, ExecutionState& state) noex const auto& input_index = stack.pop(); const auto& size = stack.pop(); - if (!check_memory(state, mem_index, size)) + if (!check_memory(state.gas_left, state.memory, mem_index, size)) return EVMC_OUT_OF_GAS; auto dst = static_cast(mem_index); @@ -453,7 +455,7 @@ inline evmc_status_code codecopy(StackTop stack, ExecutionState& state) noexcept const auto& input_index = stack.pop(); const auto& size = stack.pop(); - if (!check_memory(state, mem_index, size)) + if (!check_memory(state.gas_left, state.memory, mem_index, size)) return EVMC_OUT_OF_GAS; const auto code_size = state.original_code.size(); @@ -509,7 +511,7 @@ inline evmc_status_code extcodecopy(StackTop stack, ExecutionState& state) noexc const auto& input_index = stack.pop(); const auto& size = stack.pop(); - if (!check_memory(state, mem_index, size)) + if (!check_memory(state.gas_left, state.memory, mem_index, size)) return EVMC_OUT_OF_GAS; const auto s = static_cast(size); @@ -547,7 +549,7 @@ inline evmc_status_code returndatacopy(StackTop stack, ExecutionState& state) no const auto& input_index = stack.pop(); const auto& size = stack.pop(); - if (!check_memory(state, mem_index, size)) + if (!check_memory(state.gas_left, state.memory, mem_index, size)) return EVMC_OUT_OF_GAS; auto dst = static_cast(mem_index); @@ -640,7 +642,7 @@ inline evmc_status_code mload(StackTop stack, ExecutionState& state) noexcept { auto& index = stack.top(); - if (!check_memory(state, index, 32)) + if (!check_memory(state.gas_left, state.memory, index, 32)) return EVMC_OUT_OF_GAS; index = intx::be::unsafe::load(&state.memory[static_cast(index)]); @@ -652,7 +654,7 @@ inline evmc_status_code mstore(StackTop stack, ExecutionState& state) noexcept const auto& index = stack.pop(); const auto& value = stack.pop(); - if (!check_memory(state, index, 32)) + if (!check_memory(state.gas_left, state.memory, index, 32)) return EVMC_OUT_OF_GAS; intx::be::unsafe::store(&state.memory[static_cast(index)], value); @@ -664,7 +666,7 @@ inline evmc_status_code mstore8(StackTop stack, ExecutionState& state) noexcept const auto& index = stack.pop(); const auto& value = stack.pop(); - if (!check_memory(state, index, 1)) + if (!check_memory(state.gas_left, state.memory, index, 1)) return EVMC_OUT_OF_GAS; state.memory[static_cast(index)] = static_cast(value); @@ -901,7 +903,7 @@ inline evmc_status_code log(StackTop stack, ExecutionState& state) noexcept const auto& offset = stack.pop(); const auto& size = stack.pop(); - if (!check_memory(state, offset, size)) + if (!check_memory(state.gas_left, state.memory, offset, size)) return EVMC_OUT_OF_GAS; const auto o = static_cast(offset); @@ -970,7 +972,7 @@ inline StopToken return_impl(StackTop stack, ExecutionState& state) noexcept const auto& offset = stack[0]; const auto& size = stack[1]; - if (!check_memory(state, offset, size)) + if (!check_memory(state.gas_left, state.memory, offset, size)) return {EVMC_OUT_OF_GAS}; state.output_size = static_cast(size); diff --git a/lib/evmone/instructions_calls.cpp b/lib/evmone/instructions_calls.cpp index 9a5aa3b69b..ef94c797ba 100644 --- a/lib/evmone/instructions_calls.cpp +++ b/lib/evmone/instructions_calls.cpp @@ -30,10 +30,10 @@ evmc_status_code call_impl(StackTop stack, ExecutionState& state) noexcept return EVMC_OUT_OF_GAS; } - if (!check_memory(state, input_offset_u256, input_size_u256)) + if (!check_memory(state.gas_left, state.memory, input_offset_u256, input_size_u256)) return EVMC_OUT_OF_GAS; - if (!check_memory(state, output_offset_u256, output_size_u256)) + if (!check_memory(state.gas_left, state.memory, output_offset_u256, output_size_u256)) return EVMC_OUT_OF_GAS; const auto input_offset = static_cast(input_offset_u256); @@ -131,7 +131,7 @@ evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept stack.push(0); // Assume failure. state.return_data.clear(); - if (!check_memory(state, init_code_offset_u256, init_code_size_u256)) + if (!check_memory(state.gas_left, state.memory, init_code_offset_u256, init_code_size_u256)) return EVMC_OUT_OF_GAS; const auto init_code_offset = static_cast(init_code_offset_u256); From 390fea4ab078b74888ea251523fbbf931d8cc308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 27 Jan 2023 15:54:06 +0100 Subject: [PATCH 3/3] test: Covert hex code in a unit test --- test/unittests/evm_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unittests/evm_test.cpp b/test/unittests/evm_test.cpp index 0da94faf41..ef62f3b349 100644 --- a/test/unittests/evm_test.cpp +++ b/test/unittests/evm_test.cpp @@ -53,7 +53,7 @@ TEST_P(evm, stack_underflow) TEST_P(evm, add) { - execute(25, bytecode{"6007600d0160005260206000f3"}); + execute(25, add(7, 13) + ret_top()); EXPECT_GAS_USED(EVMC_SUCCESS, 24); EXPECT_OUTPUT_INT(20); }