diff --git a/lib/evmone/eof.cpp b/lib/evmone/eof.cpp index 3e8d465200..d6e8aa87db 100644 --- a/lib/evmone/eof.cpp +++ b/lib/evmone/eof.cpp @@ -6,11 +6,13 @@ #include "baseline_instruction_table.hpp" #include "instructions_traits.hpp" +#include #include #include #include #include #include +#include #include #include @@ -25,8 +27,9 @@ constexpr uint8_t CODE_SECTION = 0x02; constexpr uint8_t DATA_SECTION = 0x03; constexpr uint8_t MAX_SECTION = DATA_SECTION; constexpr auto CODE_SECTION_NUMBER_LIMIT = 1024; -constexpr auto MAX_STACK_HEIGHT = 0x0400; +constexpr auto MAX_STACK_HEIGHT = 0x03FF; constexpr auto OUTPUTS_INPUTS_NUMBER_LIMIT = 0x7F; +constexpr auto REL_OFFSET_SIZE = sizeof(int16_t); using EOFSectionHeaders = std::array, MAX_SECTION + 1>; @@ -230,8 +233,6 @@ EOFValidationError validate_instructions(evmc_revision rev, bytes_view code) noe /// Requires that the input is validated against truncation. bool validate_rjump_destinations(bytes_view code) noexcept { - static constexpr auto REL_OFFSET_SIZE = sizeof(int16_t); - // Collect relative jump destinations and immediate locations const auto code_size = code.size(); // list of all possible absolute rjumps destinations positions @@ -286,6 +287,120 @@ bool validate_rjump_destinations(bytes_view code) noexcept return true; } +/// Requires that the input is validated against truncation. +std::variant validate_max_stack_height( + bytes_view code, size_t func_index, const std::vector& code_types) +{ + assert(!code.empty()); + + // Special values used for detecting errors. + static constexpr int32_t LOC_UNVISITED = -1; // Unvisited byte. + static constexpr int32_t LOC_IMMEDIATE = -2; // Immediate byte. + + // Stack height in the header is limited to uint16_t, + // but keeping larger size for ease of calculation. + std::vector stack_heights(code.size(), LOC_UNVISITED); + stack_heights[0] = code_types[func_index].inputs; + + std::stack worklist; + worklist.push(0); + + while (!worklist.empty()) + { + const auto i = worklist.top(); + worklist.pop(); + + const auto opcode = static_cast(code[i]); + + auto stack_height_required = instr::traits[opcode].stack_height_required; + auto stack_height_change = instr::traits[opcode].stack_height_change; + + if (opcode == OP_CALLF) + { + const auto fid = read_uint16_be(&code[i + 1]); + + if (fid >= code_types.size()) + return EOFValidationError::invalid_code_section_index; + + stack_height_required = static_cast(code_types[fid].inputs); + stack_height_change = + static_cast(code_types[fid].outputs - stack_height_required); + } + + auto stack_height = stack_heights[i]; + assert(stack_height != LOC_UNVISITED); + + if (stack_height < stack_height_required) + return EOFValidationError::stack_underflow; + + stack_height += stack_height_change; + + // Determine size of immediate, including the special case of RJUMPV. + const size_t imm_size = (opcode == OP_RJUMPV) ? + (1 + /*count*/ size_t{code[i + 1]} * REL_OFFSET_SIZE) : + instr::traits[opcode].immediate_size; + + // Mark immediate locations. + std::fill_n(&stack_heights[i + 1], imm_size, LOC_IMMEDIATE); + + // Validates the successor instruction and updates its stack height. + const auto validate_successor = [&stack_heights, &worklist](size_t successor_offset, + int32_t expected_stack_height) { + auto& successor_stack_height = stack_heights[successor_offset]; + if (successor_stack_height == LOC_UNVISITED) + { + successor_stack_height = expected_stack_height; + worklist.push(successor_offset); + return true; + } + else + return successor_stack_height == expected_stack_height; + }; + + const auto next = i + imm_size + 1; // Offset of the next instruction (may be invalid). + + // Check validity of next instruction. We skip RJUMP and terminating instructions. + if (!instr::traits[opcode].is_terminating && opcode != OP_RJUMP) + { + if (next >= code.size()) + return EOFValidationError::no_terminating_instruction; + if (!validate_successor(next, stack_height)) + return EOFValidationError::stack_height_mismatch; + } + + // Validate non-fallthrough successors of relative jumps. + if (opcode == OP_RJUMP || opcode == OP_RJUMPI) + { + const auto target_rel_offset = read_int16_be(&code[i + 1]); + const auto target = static_cast(i) + target_rel_offset + 3; + if (!validate_successor(static_cast(target), stack_height)) + return EOFValidationError::stack_height_mismatch; + } + else if (opcode == OP_RJUMPV) + { + const auto count = code[i + 1]; + + // Insert all jump targets. + for (size_t k = 0; k < count; ++k) + { + const auto target_rel_offset = read_int16_be(&code[i + k * REL_OFFSET_SIZE + 2]); + const auto target = static_cast(next) + target_rel_offset; + if (!validate_successor(static_cast(target), stack_height)) + return EOFValidationError::stack_height_mismatch; + } + } + else if (opcode == OP_RETF && stack_height != code_types[func_index].outputs) + return EOFValidationError::non_empty_stack_on_terminating_instruction; + } + + const auto max_stack_height = *std::max_element(stack_heights.begin(), stack_heights.end()); + + if (std::find(stack_heights.begin(), stack_heights.end(), LOC_UNVISITED) != stack_heights.end()) + return EOFValidationError::unreachable_instructions; + + return max_stack_height; +} + std::variant validate_eof1( evmc_revision rev, bytes_view container) noexcept { @@ -325,6 +440,13 @@ std::variant validate_eof1( if (!validate_rjump_destinations(header.get_code(container, code_idx))) return EOFValidationError::invalid_rjump_destination; + + auto msh_or_error = + validate_max_stack_height(header.get_code(container, code_idx), code_idx, header.types); + if (const auto* error = std::get_if(&msh_or_error)) + return *error; + if (std::get(msh_or_error) != header.types[code_idx].max_stack_height) + return EOFValidationError::invalid_max_stack_height; } return header; @@ -469,6 +591,18 @@ std::string_view get_error_message(EOFValidationError err) noexcept return "max_stack_height_above_limit"; case EOFValidationError::inputs_outputs_num_above_limit: return "inputs_outputs_num_above_limit"; + case EOFValidationError::no_terminating_instruction: + return "no_terminating_instruction"; + case EOFValidationError::stack_height_mismatch: + return "stack_height_mismatch"; + case EOFValidationError::non_empty_stack_on_terminating_instruction: + return "non_empty_stack_on_terminating_instruction"; + case EOFValidationError::unreachable_instructions: + return "unreachable_instructions"; + case EOFValidationError::stack_underflow: + return "stack_underflow"; + case EOFValidationError::invalid_code_section_index: + return "invalid_code_section_index"; case EOFValidationError::impossible: return "impossible"; } diff --git a/lib/evmone/eof.hpp b/lib/evmone/eof.hpp index 87e39c4b8c..75febe7fdc 100644 --- a/lib/evmone/eof.hpp +++ b/lib/evmone/eof.hpp @@ -78,8 +78,14 @@ enum class EOFValidationError invalid_type_section_size, invalid_first_section_type, invalid_max_stack_height, + no_terminating_instruction, + stack_height_mismatch, + non_empty_stack_on_terminating_instruction, max_stack_height_above_limit, inputs_outputs_num_above_limit, + unreachable_instructions, + stack_underflow, + invalid_code_section_index, impossible, }; diff --git a/test/unittests/analysis_test.cpp b/test/unittests/analysis_test.cpp index 48cc8fadec..a7b2661c3c 100644 --- a/test/unittests/analysis_test.cpp +++ b/test/unittests/analysis_test.cpp @@ -258,7 +258,7 @@ TEST(analysis, jumpdests_groups) TEST(analysis, example1_eof1) { const auto code = eof1_bytecode( - push(0x2a) + push(0x1e) + OP_MSTORE8 + OP_MSIZE + push(0) + OP_SSTORE, "deadbeef"); + push(0x2a) + push(0x1e) + OP_MSTORE8 + OP_MSIZE + push(0) + OP_SSTORE, 2, "deadbeef"); const auto header = evmone::read_valid_eof1_header(code); const auto analysis = analyze(EVMC_CANCUN, header.get_code(code, 0)); diff --git a/test/unittests/eof_validation_test.cpp b/test/unittests/eof_validation_test.cpp index 9a8aef3bab..39a5dcf41d 100644 --- a/test/unittests/eof_validation_test.cpp +++ b/test/unittests/eof_validation_test.cpp @@ -221,16 +221,16 @@ TEST(eof_validation, EOF1_truncated_section) TEST(eof_validation, EOF1_code_section_offset) { const auto eof = - "EF0001 010008 02000200020001 030004 00 0000000000000000 fefe fe 0000 0000"_hex; + "EF0001 010008 02000200030001 030004 00 00000001 00000000 6001fe fe 0000 0000"_hex; ASSERT_EQ(validate_eof(EVMC_CANCUN, eof), EOFValidationError::success); const auto header = read_valid_eof1_header(eof); ASSERT_EQ(header.code_sizes.size(), 2); - EXPECT_EQ(header.code_sizes[0], 2); + EXPECT_EQ(header.code_sizes[0], 3); EXPECT_EQ(header.code_sizes[1], 1); ASSERT_EQ(header.code_offsets.size(), 2); EXPECT_EQ(header.code_offsets[0], 25); - EXPECT_EQ(header.code_offsets[1], 27); + EXPECT_EQ(header.code_offsets[1], 28); } TEST(eof_validation, EOF1_trailing_bytes) @@ -313,8 +313,6 @@ TEST(eof_validation, EOF1_too_many_code_sections) TEST(eof_validation, EOF1_undefined_opcodes) { - auto cont = "EF0001 010004 0200010002 030000 00 00000000 0000"_hex; - const auto& gas_table = evmone::instr::gas_costs[EVMC_CANCUN]; for (uint16_t opcode = 0; opcode <= 0xff; ++opcode) @@ -331,12 +329,34 @@ TEST(eof_validation, EOF1_undefined_opcodes) opcode == OP_SELFDESTRUCT) continue; - cont[cont.size() - 2] = static_cast(opcode); + auto cont = + "EF0001 010004 0200010014 030000 00 00000000 6001" + "80808080808080808080808080808080 " + ""_hex; + + if (opcode == OP_RETF) + { + cont += "5050505050505050505050505050505050"_hex; + cont += static_cast(opcode); + cont[10] = 0x24; + } + else + { + cont += static_cast(opcode); + if (!instr::traits[opcode].is_terminating) + cont += "00"_hex; + else + cont[10] = 0x13; + } + + auto op_stack_change = instr::traits[opcode].stack_height_change; + cont[18] = static_cast(op_stack_change <= 0 ? 17 : 17 + op_stack_change); const auto expected = (gas_table[opcode] == evmone::instr::undefined ? EOFValidationError::undefined_instruction : EOFValidationError::success); - EXPECT_EQ(validate_eof(cont), expected) << hex(cont); + auto result = validate_eof(cont); + EXPECT_EQ(result, expected) << hex(cont); } EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030000 00 00000000 FE"), @@ -362,6 +382,9 @@ TEST(eof_validation, EOF1_truncated_push) const bytes code{opcode + bytes(required_bytes, 0) + uint8_t{OP_STOP}}; code_size_byte = static_cast(code.size()); + + eof_header[18] = static_cast(instr::traits[opcode].stack_height_change); + const auto container = eof_header + code; EXPECT_EQ(validate_eof(container), EOFValidationError::success) << hex(container); @@ -375,26 +398,26 @@ TEST(eof_validation, EOF1_valid_rjump) EOFValidationError::success); // offset = 3 - EXPECT_EQ(validate_eof("EF0001 010004 0200010007 030000 00 00000000 5C000300000000"), + EXPECT_EQ(validate_eof("EF0001 010004 0200010009 030000 00 00000001 5C00036001005CFFFA"), EOFValidationError::success); // offset = -4 - EXPECT_EQ(validate_eof("EF0001 010004 0200010005 030000 00 00000000 005CFFFC00"), + EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5B5CFFFC"), EOFValidationError::success); } TEST(eof_validation, EOF1_valid_rjumpi) { // offset = 0 - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005D000000"), + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000001 60005D000000"), EOFValidationError::success); // offset = 3 - EXPECT_EQ(validate_eof("EF0001 010004 0200010009 030000 00 00000000 60005D000300000000"), + EXPECT_EQ(validate_eof("EF0001 010004 0200010009 030000 00 00000001 60005D00035B5B5B00"), EOFValidationError::success); // offset = -5 - EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005DFFFB00"), + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000001 60005DFFFB00"), EOFValidationError::success); } @@ -602,6 +625,112 @@ TEST(eof_validation, deprecated_instructions) } } +TEST(eof_valication, max_arguments_count) +{ + EXPECT_EQ(validate_eof("EF0001 010008 02000200010001 030000 00 00000000 7F7F007F B1 B1"), + EOFValidationError::success); + + EXPECT_EQ(validate_eof("EF0001 010008 02000200010001 030000 00 00000000 80800080 B1 B1"), + EOFValidationError::inputs_outputs_num_above_limit); + + { + auto code = "EF0001 010008 020002000100FF 030000 00 00000000 007F007F B1" + + 127 * bytecode{1} + OP_RETF; + + EXPECT_EQ(validate_eof(code), EOFValidationError::success); + } + + { + auto code = "EF0001 010008 02000200010101 030000 00 00000000 00800080 B1" + + 128 * bytecode{1} + OP_RETF; + + EXPECT_EQ(validate_eof(code), EOFValidationError::inputs_outputs_num_above_limit); + } + + { + auto code = + "EF0001 010008 02000200010080 030000 00 00000000 7F00007F B1" + 127 * OP_POP + OP_RETF; + + EXPECT_EQ(validate_eof(code), EOFValidationError::success); + } + + { + auto code = + "EF0001 010008 02000200010081 030000 00 00000000 80000080 B1" + 128 * OP_POP + OP_RETF; + + EXPECT_EQ(validate_eof(code), EOFValidationError::inputs_outputs_num_above_limit); + } +} + +TEST(eof_valication, max_stack_heigh) +{ + { + auto code = "EF0001 010008 02000200010BFE 030000 00 00000000 000003FF B1" + + 0x3FF * bytecode{1} + 0x3FF * OP_POP + OP_RETF; + + EXPECT_EQ(validate_eof(code), EOFValidationError::success); + } + + { + auto code = "EF0001 010008 0200020BFE0001 030000 00 000003FF 00000000" + + 0x3FF * bytecode{1} + 0x3FF * OP_POP + OP_RETF + OP_RETF; + + EXPECT_EQ(validate_eof(code), EOFValidationError::success); + } + + { + auto code = "EF0001 010008 02000200010C01 030000 00 00000000 00000400 B1" + + 0x400 * bytecode{1} + 0x400 * OP_POP + OP_RETF; + + EXPECT_EQ(validate_eof(code), EOFValidationError::max_stack_height_above_limit); + } + + { + auto code = "EF0001 010008 0200020C010001 030000 00 00000400 00000000" + + 0x400 * bytecode{1} + 0x400 * OP_POP + OP_RETF + OP_RETF; + + EXPECT_EQ(validate_eof(code), EOFValidationError::max_stack_height_above_limit); + } + + { + auto code = "EF0001 010008 02000200010C01 030000 00 00000000 000003FF B1" + + 0x400 * bytecode{1} + 0x400 * OP_POP + OP_RETF; + + EXPECT_EQ(validate_eof(code), EOFValidationError::invalid_max_stack_height); + } + + { + auto code = "EF0001 010008 0200020C010001 030000 00 000003FF 00000000" + + 0x400 * bytecode{1} + 0x400 * OP_POP + OP_RETF + OP_RETF; + + EXPECT_EQ(validate_eof(code), EOFValidationError::invalid_max_stack_height); + } + + { + auto code = eof1_bytecode(rjumpi(2, 0) + 1 + OP_RETF, 1); + + EXPECT_EQ(validate_eof(code), EOFValidationError::stack_height_mismatch); + } + + { + auto code = eof1_bytecode(rjumpi(-3, 0) + OP_RETF, 1); + + EXPECT_EQ(validate_eof(code), EOFValidationError::stack_height_mismatch); + } + + { + auto code = eof1_bytecode(rjumpv({-4}, 0) + OP_RETF, 1); + + EXPECT_EQ(validate_eof(code), EOFValidationError::stack_height_mismatch); + } +} + +TEST(eof_validation, callf_invalid_code_section_index) +{ + EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 b0000100"), + EOFValidationError::invalid_code_section_index); +} + TEST(eof_validation, incomplete_section_size) { EXPECT_EQ( diff --git a/test/unittests/evm_eof_rjump_test.cpp b/test/unittests/evm_eof_rjump_test.cpp index 55720b8940..9c71ee990c 100644 --- a/test/unittests/evm_eof_rjump_test.cpp +++ b/test/unittests/evm_eof_rjump_test.cpp @@ -15,14 +15,15 @@ TEST_P(evm, eof1_rjump) return; rev = EVMC_CANCUN; - auto code = eof1_bytecode(rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1)); + auto code = eof1_bytecode(rjumpi(3, 0) + rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1), 2); execute(code); EXPECT_STATUS(EVMC_SUCCESS); ASSERT_EQ(result.output_size, 1); EXPECT_EQ(result.output_data[0], 1); - code = eof1_bytecode(rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1), "deadbeef"); + code = eof1_bytecode( + rjumpi(3, 0) + rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1), 2, "deadbeef"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -37,16 +38,14 @@ TEST_P(evm, eof1_rjump_backward) return; rev = EVMC_CANCUN; - auto code = - eof1_bytecode(rjump(11) + OP_INVALID + mstore8(0, 1) + ret(0, 1) + rjump(-13) + OP_STOP); + auto code = eof1_bytecode(rjump(10) + mstore8(0, 1) + ret(0, 1) + rjump(-13), 2); execute(code); EXPECT_STATUS(EVMC_SUCCESS); ASSERT_EQ(result.output_size, 1); EXPECT_EQ(result.output_data[0], 1); - code = eof1_bytecode( - rjump(11) + OP_INVALID + mstore8(0, 1) + ret(0, 1) + rjump(-13) + OP_STOP, "deadbeef"); + code = eof1_bytecode(rjump(10) + mstore8(0, 1) + ret(0, 1) + rjump(-13), 2, "deadbeef"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -61,7 +60,7 @@ TEST_P(evm, eof1_rjump_0_offset) return; rev = EVMC_CANCUN; - auto code = eof1_bytecode(rjump(0) + mstore8(0, 1) + ret(0, 1)); + auto code = eof1_bytecode(rjump(0) + mstore8(0, 1) + ret(0, 1), 2); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -77,7 +76,7 @@ TEST_P(evm, eof1_rjumpi) rev = EVMC_CANCUN; auto code = eof1_bytecode( - rjumpi(10, calldataload(0)) + mstore8(0, 2) + ret(0, 1) + mstore8(0, 1) + ret(0, 1)); + rjumpi(10, calldataload(0)) + mstore8(0, 2) + ret(0, 1) + mstore8(0, 1) + ret(0, 1), 2); // RJUMPI condition is true execute(code, "01"_hex); @@ -99,8 +98,9 @@ TEST_P(evm, eof1_rjumpi_backwards) return; rev = EVMC_CANCUN; - auto code = eof1_bytecode(rjump(11) + OP_INVALID + mstore8(0, 1) + ret(0, 1) + - rjumpi(-16, calldataload(0)) + mstore8(0, 2) + ret(0, 1)); + auto code = eof1_bytecode(rjump(10) + mstore8(0, 1) + ret(0, 1) + rjumpi(-16, calldataload(0)) + + mstore8(0, 2) + ret(0, 1), + 2); // RJUMPI condition is true execute(code, "01"_hex); @@ -122,7 +122,7 @@ TEST_P(evm, eof1_rjumpi_0_offset) return; rev = EVMC_CANCUN; - auto code = eof1_bytecode(rjumpi(0, calldataload(0)) + mstore8(0, 1) + ret(0, 1)); + auto code = eof1_bytecode(rjumpi(0, calldataload(0)) + mstore8(0, 1) + ret(0, 1), 2); // RJUMPI condition is true execute(code, "01"_hex); @@ -146,7 +146,7 @@ TEST_P(evm, eof1_rjumpv_single_offset) rev = EVMC_CANCUN; auto code = eof1_bytecode(rjumpv({3}, 0) + OP_JUMPDEST + OP_JUMPDEST + OP_STOP + 20 + 40 + 0 + OP_CODECOPY + ret(0, 20), - "ef000101000402000100010300000000000000fe"); + 3, "ef000101000402000100010300000000000000fe"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -165,7 +165,7 @@ TEST_P(evm, eof1_rjumpv_multiple_offsets) auto code = eof1_bytecode(rjump(12) + 10 + 68 + 0 + OP_CODECOPY + ret(0, 10) + rjumpv({12, -22, 0}, 1) + 10 + 78 + 0 + OP_CODECOPY + ret(0, 10) + 20 + 68 + 0 + OP_CODECOPY + ret(0, 20), - "ef000101000402000100010300000000000000fe"); + 3, "ef000101000402000100010300000000000000fe"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); diff --git a/test/unittests/evm_eof_test.cpp b/test/unittests/evm_eof_test.cpp index 23cbb14a42..dc31cbc2d3 100644 --- a/test/unittests/evm_eof_test.cpp +++ b/test/unittests/evm_eof_test.cpp @@ -24,7 +24,7 @@ TEST_P(evm, eof1_execution_with_data_section) { rev = EVMC_CANCUN; // data section contains ret(0, 1) - const auto code = eof1_bytecode(mstore8(0, 1) + OP_STOP, ret(0, 1)); + const auto code = eof1_bytecode(mstore8(0, 1) + OP_STOP, 2, ret(0, 1)); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -34,14 +34,14 @@ TEST_P(evm, eof1_execution_with_data_section) TEST_P(evm, eof1_codesize) { rev = EVMC_CANCUN; - auto code = eof1_bytecode(mstore8(0, OP_CODESIZE) + ret(0, 1)); + auto code = eof1_bytecode(mstore8(0, OP_CODESIZE) + ret(0, 1), 2); execute(code); EXPECT_STATUS(EVMC_SUCCESS); ASSERT_EQ(result.output_size, 1); EXPECT_EQ(result.output_data[0], 28); - code = eof1_bytecode(mstore8(0, OP_CODESIZE) + ret(0, 1), "deadbeef"); + code = eof1_bytecode(mstore8(0, OP_CODESIZE) + ret(0, 1), 2, "deadbeef"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -52,32 +52,32 @@ TEST_P(evm, eof1_codesize) TEST_P(evm, eof1_codecopy_full) { rev = EVMC_CANCUN; - auto code = eof1_bytecode(bytecode{31} + 0 + 0 + OP_CODECOPY + ret(0, 31)); + auto code = eof1_bytecode(bytecode{31} + 0 + 0 + OP_CODECOPY + ret(0, 31), 3); execute(code); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(bytes_view(result.output_data, result.output_size), - "ef0001010004020001000c0300000000000000601f6000600039601f6000f3"_hex); + "ef0001010004020001000c0300000000000003601f6000600039601f6000f3"_hex); - code = eof1_bytecode(bytecode{35} + 0 + 0 + OP_CODECOPY + ret(0, 35), "deadbeef"); + code = eof1_bytecode(bytecode{35} + 0 + 0 + OP_CODECOPY + ret(0, 35), 3, "deadbeef"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(bytes_view(result.output_data, result.output_size), - "ef0001010004020001000c03000400000000006023600060003960236000f3deadbeef"_hex); + "ef0001010004020001000c03000400000000036023600060003960236000f3deadbeef"_hex); } TEST_P(evm, eof1_codecopy_header) { rev = EVMC_CANCUN; - auto code = eof1_bytecode(bytecode{15} + 0 + 0 + OP_CODECOPY + ret(0, 15)); + auto code = eof1_bytecode(bytecode{15} + 0 + 0 + OP_CODECOPY + ret(0, 15), 3); execute(code); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ( bytes_view(result.output_data, result.output_size), "ef0001010004020001000c03000000"_hex); - code = eof1_bytecode(bytecode{15} + 0 + 0 + OP_CODECOPY + ret(0, 15), "deadbeef"); + code = eof1_bytecode(bytecode{15} + 0 + 0 + OP_CODECOPY + ret(0, 15), 3, "deadbeef"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -88,13 +88,13 @@ TEST_P(evm, eof1_codecopy_header) TEST_P(evm, eof1_codecopy_code) { rev = EVMC_CANCUN; - auto code = eof1_bytecode(bytecode{12} + 19 + 0 + OP_CODECOPY + ret(0, 12)); + auto code = eof1_bytecode(bytecode{12} + 19 + 0 + OP_CODECOPY + ret(0, 12), 3); execute(code); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(bytes_view(result.output_data, result.output_size), "600c6013600039600c6000f3"_hex); - code = eof1_bytecode(bytecode{12} + 19 + 0 + OP_CODECOPY + ret(0, 12), "deadbeef"); + code = eof1_bytecode(bytecode{12} + 19 + 0 + OP_CODECOPY + ret(0, 12), 3, "deadbeef"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -105,7 +105,7 @@ TEST_P(evm, eof1_codecopy_data) { rev = EVMC_CANCUN; - const auto code = eof1_bytecode(bytecode{4} + 31 + 0 + OP_CODECOPY + ret(0, 4), "deadbeef"); + const auto code = eof1_bytecode(bytecode{4} + 31 + 0 + OP_CODECOPY + ret(0, 4), 3, "deadbeef"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); @@ -116,19 +116,19 @@ TEST_P(evm, eof1_codecopy_out_of_bounds) { // 4 bytes out of container bounds - result is implicitly 0-padded rev = EVMC_CANCUN; - auto code = eof1_bytecode(bytecode{35} + 0 + 0 + OP_CODECOPY + ret(0, 35)); + auto code = eof1_bytecode(bytecode{35} + 0 + 0 + OP_CODECOPY + ret(0, 35), 3); execute(code); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(bytes_view(result.output_data, result.output_size), - "ef0001010004020001000c03000000000000006023600060003960236000f300000000"_hex); + "ef0001010004020001000c03000000000000036023600060003960236000f300000000"_hex); - code = eof1_bytecode(bytecode{39} + 0 + 0 + OP_CODECOPY + ret(0, 39), "deadbeef"); + code = eof1_bytecode(bytecode{39} + 0 + 0 + OP_CODECOPY + ret(0, 39), 3, "deadbeef"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); EXPECT_EQ(bytes_view(result.output_data, result.output_size), - "ef0001010004020001000c03000400000000006027600060003960276000f3deadbeef00000000"_hex); + "ef0001010004020001000c03000400000000036027600060003960276000f3deadbeef00000000"_hex); } TEST_P(evm, eof_data_only_contract) diff --git a/test/unittests/tracing_test.cpp b/test/unittests/tracing_test.cpp index caed32d3fd..7dbc25df0d 100644 --- a/test/unittests/tracing_test.cpp +++ b/test/unittests/tracing_test.cpp @@ -297,7 +297,7 @@ TEST_F(tracing, trace_eof) vm.add_tracer(evmone::create_instruction_tracer(trace_stream)); trace_stream << '\n'; - EXPECT_EQ(trace(eof1_bytecode(add(2, 3) + OP_STOP), 0, 0, EVMC_CANCUN), R"( + EXPECT_EQ(trace(eof1_bytecode(add(2, 3) + OP_STOP, 2), 0, 0, EVMC_CANCUN), R"( {"depth":0,"rev":"Cancun","static":false} {"pc":0,"op":96,"opName":"PUSH1","gas":1000000,"stack":[],"memorySize":0} {"pc":2,"op":96,"opName":"PUSH1","gas":999997,"stack":["0x3"],"memorySize":0} diff --git a/test/utils/bytecode.hpp b/test/utils/bytecode.hpp index 94747ff36d..d5f556d435 100644 --- a/test/utils/bytecode.hpp +++ b/test/utils/bytecode.hpp @@ -90,27 +90,29 @@ big_endian(T value) return {static_cast(value >> 8), static_cast(value)}; } -inline bytecode eof_header(uint8_t version, uint16_t code_size, uint16_t data_size) +inline bytecode eof_header( + uint8_t version, uint16_t code_size, uint16_t max_stack_height, uint16_t data_size) { bytecode out{bytes{0xEF, 0x00, version}}; out += "01" + big_endian(uint16_t{4}); // type header out += "02"_hex + big_endian(uint16_t{1}) + big_endian(code_size); out += "03" + big_endian(data_size); out += "00"; - out += "00000000"; // type section + out += "0000"_hex + big_endian(max_stack_height); // type section return out; } -inline bytecode eof1_header(uint16_t code_size, uint16_t data_size = 0) +inline bytecode eof1_header(uint16_t code_size, uint16_t max_stack_height, uint16_t data_size = 0) { - return eof_header(1, code_size, data_size); + return eof_header(1, code_size, max_stack_height, data_size); } -inline bytecode eof1_bytecode(bytecode code, bytecode data = {}) +inline bytecode eof1_bytecode(bytecode code, uint16_t max_stack_height = 0, bytecode data = {}) { assert(code.size() <= std::numeric_limits::max()); assert(data.size() <= std::numeric_limits::max()); - return eof1_header(static_cast(code.size()), static_cast(data.size())) + + return eof1_header(static_cast(code.size()), max_stack_height, + static_cast(data.size())) + code + data; }