diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index 12d57b397a..33fbb1183a 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -569,25 +569,22 @@ inline Result extcodecopy(StackTop stack, int64_t gas_left, ExecutionState& stat return {EVMC_SUCCESS, gas_left}; } -inline Result returndataload(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept +inline void returndataload(StackTop stack, ExecutionState& state) noexcept { auto& index = stack.top(); if (state.return_data.size() < index) - return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; + index = 0; else { const auto begin = static_cast(index); - const auto end = begin + 32; - if (state.return_data.size() < end) - return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; + const auto end = std::min(begin + 32, state.return_data.size()); uint8_t data[32] = {}; for (size_t i = 0; i < (end - begin); ++i) data[i] = state.return_data[begin + i]; index = intx::be::unsafe::load(data); - return {EVMC_SUCCESS, gas_left}; } } @@ -608,18 +605,36 @@ inline Result returndatacopy(StackTop stack, int64_t gas_left, ExecutionState& s auto dst = static_cast(mem_index); auto s = static_cast(size); - if (state.return_data.size() < input_index) - return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; - auto src = static_cast(input_index); + if (is_eof_container(state.original_code)) + { + auto src = state.return_data.size() < input_index ? state.return_data.size() : + static_cast(input_index); + auto copy_size = std::min(s, state.return_data.size() - src); - if (src + s > state.return_data.size()) - return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; - if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) - return {EVMC_OUT_OF_GAS, gas_left}; + if (copy_size > 0) + std::memcpy(&state.memory[dst], &state.return_data[src], copy_size); - if (s > 0) - std::memcpy(&state.memory[dst], &state.return_data[src], s); + if (s - copy_size > 0) + std::memset(&state.memory[dst + copy_size], 0, s - copy_size); + } + else + { + if (state.return_data.size() < input_index) + return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; + auto src = static_cast(input_index); + + if (src + s > state.return_data.size()) + return {EVMC_INVALID_MEMORY_ACCESS, gas_left}; + + if (const auto cost = copy_cost(s); (gas_left -= cost) < 0) + return {EVMC_OUT_OF_GAS, gas_left}; + + if (s > 0) + std::memcpy(&state.memory[dst], &state.return_data[src], s); + } return {EVMC_SUCCESS, gas_left}; } diff --git a/test/unittests/evm_eof_calls_test.cpp b/test/unittests/evm_eof_calls_test.cpp index 9798fa6b1e..3800e3260c 100644 --- a/test/unittests/evm_eof_calls_test.cpp +++ b/test/unittests/evm_eof_calls_test.cpp @@ -340,72 +340,111 @@ TEST_P(evm, returndataload_outofrange) rev = EVMC_PRAGUE; { - const uint8_t call_output[31]{}; + const auto call_output = + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex; // 31 bytes host.call_result.output_data = std::data(call_output); host.call_result.output_size = std::size(call_output); - execute(eof_bytecode(extstaticcall(0) + returndataload(0) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(0) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00"_hex); } { - const uint8_t call_output[32]{}; + const auto call_output = + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex; // 32 bytes host.call_result.output_data = std::data(call_output); host.call_result.output_size = std::size(call_output); - execute(eof_bytecode(extstaticcall(0) + returndataload(1) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(1) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00"_hex); - execute(eof_bytecode(extstaticcall(0) + returndataload(31) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(31) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaa00000000000000000000000000000000000000000000000000000000000000"_hex); - execute(eof_bytecode(extstaticcall(0) + returndataload(32) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(32) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0x0000000000000000000000000000000000000000000000000000000000000000"_hex); - execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0x0000000000000000000000000000000000000000000000000000000000000000"_hex); - execute(eof_bytecode(extstaticcall(0) + returndataload(0) + OP_STOP, 3)); + execute(eof_bytecode(extstaticcall(0) + returndataload(0) + ret_top(), 3)); EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); } { - const uint8_t call_output[34]{}; + // 34 bytes + const auto call_output = + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex; host.call_result.output_data = std::data(call_output); host.call_result.output_size = std::size(call_output); - execute(eof_bytecode(extstaticcall(0) + returndataload(3) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(3) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00"_hex); - execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0x0000000000000000000000000000000000000000000000000000000000000000"_hex); - execute(eof_bytecode(extstaticcall(0) + returndataload(1) + OP_STOP, 3)); + execute(eof_bytecode(extstaticcall(0) + returndataload(1) + ret_top(), 3)); EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); - execute(eof_bytecode(extstaticcall(0) + returndataload(2) + OP_STOP, 3)); + execute(eof_bytecode(extstaticcall(0) + returndataload(2) + ret_top(), 3)); EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); } { - const uint8_t call_output[64]{}; + const auto call_output = + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex; // 64 bytes host.call_result.output_data = std::data(call_output); host.call_result.output_size = std::size(call_output); - execute(eof_bytecode(extstaticcall(0) + returndataload(33) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(33) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00"_hex); - execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0x0000000000000000000000000000000000000000000000000000000000000000"_hex); - execute(eof_bytecode(extstaticcall(0) + returndataload(1) + OP_STOP, 3)); + execute(eof_bytecode(extstaticcall(0) + returndataload(1) + ret_top(), 3)); EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); - execute(eof_bytecode(extstaticcall(0) + returndataload(31) + OP_STOP, 3)); + execute(eof_bytecode(extstaticcall(0) + returndataload(31) + ret_top(), 3)); EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); - execute(eof_bytecode(extstaticcall(0) + returndataload(32) + OP_STOP, 3)); + execute(eof_bytecode(extstaticcall(0) + returndataload(32) + ret_top(), 3)); EXPECT_EQ(result.status_code, EVMC_SUCCESS); - execute(eof_bytecode(extstaticcall(0) + returndataload(0) + OP_STOP, 3)); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); + + execute(eof_bytecode(extstaticcall(0) + returndataload(0) + ret_top(), 3)); EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"_hex); } } @@ -416,14 +455,17 @@ TEST_P(evm, returndataload_empty) return; rev = EVMC_PRAGUE; - execute(eof_bytecode(extstaticcall(0) + returndataload(0) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(0) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), evmc::bytes32(0)); - execute(eof_bytecode(extstaticcall(0) + returndataload(1) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(1) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), evmc::bytes32(0)); - execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(max_uint256) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), evmc::bytes32(0)); } TEST_P(evm, returndataload_outofrange_highbits) @@ -440,8 +482,9 @@ TEST_P(evm, returndataload_outofrange_highbits) // Covers an incorrect cast of RETURNDATALOAD arg to `size_t` ignoring the high bits. const auto highbits = 0x1000000000000000000000000000000000000000000000000000000000000000_bytes32; - execute(eof_bytecode(extstaticcall(0) + returndataload(highbits) + OP_STOP, 3)); - EXPECT_EQ(result.status_code, EVMC_INVALID_MEMORY_ACCESS); + execute(eof_bytecode(extstaticcall(0) + returndataload(highbits) + ret_top(), 3)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), evmc::bytes32(0)); } TEST_P(evm, extcall_gas_refund_aggregation_different_calls) @@ -491,3 +534,138 @@ TEST_P(evm, extcall_gas_refund_aggregation_same_calls) EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(result.gas_refund, 2); } + +TEST_P(evm, eof_returndatacopy) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const auto call_output = + 0x497f3c9f61479c1cfa53f0373d39d2bf4e5f73f71411da62f1d6b85c03a60735_bytes32; + host.call_result.output_data = std::data(call_output.bytes); + host.call_result.output_size = std::size(call_output.bytes); + + const auto code = eof_bytecode(extcall(0) + returndatacopy(0, 0, 32) + ret(0, 32), 4); + execute(safe_call_gas, code); + + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), call_output); +} + +TEST_P(evm, eof_returndatacopy_empty) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + execute(eof_bytecode(extcall(0) + returndatacopy(0, 0, 0) + ret(0, 32), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_OUTPUT_INT(0); +} + +TEST_P(evm, eof_returndatacopy_oog) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const uint8_t call_output[1]{}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + constexpr auto retained_gas = 5000; + constexpr auto gas = 3 * 3 + 100 + retained_gas * 64; + // Uses OP_JUMPDEST to burn gas retained by the caller. + execute(gas, + eof_bytecode(extstaticcall(0) + (retained_gas - 3 * 3 - 3 - 1 * 3 - 1 * 3) * OP_JUMPDEST + + returndatacopy(0, 0, 1) + OP_STOP, + 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + + execute(gas, eof_bytecode(extstaticcall(0) + + (retained_gas - 3 * 3 - 3 - 1 * 3 - 1 * 3 + 1) * OP_JUMPDEST + + returndatacopy(0, 0, 1) + OP_STOP, + 4)); + EXPECT_EQ(result.status_code, EVMC_OUT_OF_GAS); +} + +TEST_P(evm, eof_returndatacopy_cost) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const uint8_t call_output[1]{}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + host.call_result.gas_left = 0; + + execute(eof_bytecode(extstaticcall(0) + returndatacopy(0, 0, 1) + OP_STOP, 4)); + const auto gas_with = gas_used; + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + execute(eof_bytecode(extstaticcall(0) + 3 * push(0) + OP_STOP, 4)); + EXPECT_GAS_USED(EVMC_SUCCESS, gas_with - 3 - 1 * 3 - 1 * 3); +} + +TEST_P(evm, eof_returndatacopy_outofrange) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const uint8_t call_output[2]{0xab, 0xcd}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + execute(safe_call_gas, eof_bytecode(extstaticcall(0) + returndatacopy(0, 0, 3) + ret(0, 3), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0xabcd00"_hex); + + execute(safe_call_gas, eof_bytecode(extstaticcall(0) + returndatacopy(0, 1, 2) + ret(0, 2), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0xcd00"_hex); + + execute(safe_call_gas, eof_bytecode(extstaticcall(0) + returndatacopy(0, 2, 1) + ret(0, 1), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0x00"_hex); + + execute(safe_call_gas, eof_bytecode(extstaticcall(0) + returndatacopy(0, 3, 1) + ret(0, 1), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0x00"_hex); + + execute(safe_call_gas, + eof_bytecode(extstaticcall(0) + returndatacopy(0, 3, 0) + ret(0, OP_MSIZE), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0x"_hex); +} + +TEST_P(evm, eof_returndatacopy_outofrange_highbits) +{ + // Not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_PRAGUE; + const uint8_t call_output[2]{0xab, 0xcd}; + host.call_result.output_data = std::data(call_output); + host.call_result.output_size = std::size(call_output); + + const auto highbits = + 0x1000000000000000000000000000000000000000000000000000000000000000_bytes32; + execute(safe_call_gas, + eof_bytecode(extstaticcall(0) + returndatacopy(0, highbits, 0) + ret(0, OP_MSIZE), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0x"_hex); + + execute(safe_call_gas, + eof_bytecode(extstaticcall(0) + returndatacopy(0, highbits, 1) + ret(0, 1), 4)); + EXPECT_EQ(result.status_code, EVMC_SUCCESS); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "0x00"_hex); +}