diff --git a/lib/evmone/CMakeLists.txt b/lib/evmone/CMakeLists.txt index b97c325cd7..bffddd35b9 100644 --- a/lib/evmone/CMakeLists.txt +++ b/lib/evmone/CMakeLists.txt @@ -41,7 +41,7 @@ if(CABLE_COMPILER_GNULIKE) target_compile_options( evmone PRIVATE -fno-exceptions - $<$:-Wstack-usage=2500> + $<$:-Wstack-usage=2600> ) if(NOT SANITIZE MATCHES undefined) # RTTI can be disabled except for UBSan which checks vptr integrity. diff --git a/lib/evmone/advanced_execution.cpp b/lib/evmone/advanced_execution.cpp index f7e7f5801e..a72a6e4a5a 100644 --- a/lib/evmone/advanced_execution.cpp +++ b/lib/evmone/advanced_execution.cpp @@ -36,7 +36,7 @@ evmc_result execute(evmc_vm* /*unused*/, const evmc_host_interface* host, evmc_h if (rev >= EVMC_CANCUN) { const auto eof1_header = read_valid_eof1_header(container); - analysis = analyze(rev, {&container[eof1_header.code_begin()], eof1_header.code_size}); + analysis = analyze(rev, eof1_header.get_code(container, 0)); } else // Skip analysis, because it will recognize 01 section id as OP_ADD and return diff --git a/lib/evmone/advanced_instructions.cpp b/lib/evmone/advanced_instructions.cpp index b484912f2d..936411fee4 100644 --- a/lib/evmone/advanced_instructions.cpp +++ b/lib/evmone/advanced_instructions.cpp @@ -245,6 +245,12 @@ constexpr std::array instruction_implementations = []( table[OP_CREATE2] = op_create; table[OP_STATICCALL] = op_call; + table[OP_RJUMP] = op_undefined; + table[OP_RJUMPI] = op_undefined; + table[OP_RJUMPV] = op_undefined; + table[OP_CALLF] = op_undefined; + table[OP_RETF] = op_undefined; + table[OP_DUPN] = op_undefined; table[OP_SWAPN] = op_undefined; diff --git a/lib/evmone/baseline.cpp b/lib/evmone/baseline.cpp index 99cd420d12..7a9caab651 100644 --- a/lib/evmone/baseline.cpp +++ b/lib/evmone/baseline.cpp @@ -67,10 +67,29 @@ CodeAnalysis analyze_legacy(bytes_view code) return {pad_code(code), code.size(), analyze_jumpdests(code)}; } -CodeAnalysis analyze_eof1(bytes_view eof_container, const EOF1Header& header) +CodeAnalysis analyze_eof1(bytes_view container) { - const auto executable_code = eof_container.substr(header.code_begin(), header.code_size); - return {executable_code, analyze_jumpdests(executable_code)}; + const auto header = read_valid_eof1_header(container); + + // Extract all code sections as single buffer reference. + // TODO: It would be much easier if header had code_sections_offset and data_section_offset + // with code_offsets[] being relative to code_sections_offset. + const auto code_sections_offset = header.code_offsets[0]; + const auto code_sections_end = size_t{header.code_offsets.back()} + header.code_sizes.back(); + const auto executable_code = + container.substr(code_sections_offset, code_sections_end - code_sections_offset); + + // Code section offsets relative to the beginning of the first code section. + // Execution starts at position 0 (first code section). + // The implementation of CALLF uses these offsets. + CodeAnalysis::CodeOffsets relative_offsets; + relative_offsets.reserve(header.code_offsets.size()); + for (const auto offset : header.code_offsets) + relative_offsets.push_back(offset - code_sections_offset); + + // FIXME: Better way of getting EOF version. + const auto eof_version = container[2]; + return CodeAnalysis{executable_code, {}, eof_version, relative_offsets}; } } // namespace @@ -78,9 +97,7 @@ CodeAnalysis analyze(evmc_revision rev, bytes_view code) { if (rev < EVMC_CANCUN || !is_eof_container(code)) return analyze_legacy(code); - - const auto eof1_header = read_valid_eof1_header(code); - return analyze_eof1(code, eof1_header); + return analyze_eof1(code); } namespace @@ -321,7 +338,7 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana const auto code = analysis.executable_code; - const auto& cost_table = get_baseline_cost_table(state.rev); + const auto& cost_table = get_baseline_cost_table(state.rev, analysis.eof_version); auto* tracer = vm.get_tracer(); if (INTX_UNLIKELY(tracer != nullptr)) diff --git a/lib/evmone/baseline.hpp b/lib/evmone/baseline.hpp index 9f9a36cca2..1519534bc7 100644 --- a/lib/evmone/baseline.hpp +++ b/lib/evmone/baseline.hpp @@ -22,9 +22,14 @@ class CodeAnalysis { public: using JumpdestMap = std::vector; + using CodeOffsets = std::vector; bytes_view executable_code; ///< Executable code section. JumpdestMap jumpdest_map; ///< Map of valid jump destinations. + uint8_t eof_version = 0; ///< The EOF version, 0 means legacy code. + /// Offset of each code section relative to the beginning of the first code + /// section. We flatten the sections for cheap execution. + CodeOffsets code_offsets; private: /// Padded code for faster legacy code execution. @@ -38,8 +43,11 @@ class CodeAnalysis m_padded_code{std::move(padded_code)} {} - CodeAnalysis(bytes_view code, JumpdestMap map) - : executable_code{code}, jumpdest_map{std::move(map)} + CodeAnalysis(bytes_view code, JumpdestMap map, uint8_t version, CodeOffsets offsets) + : executable_code{code}, + jumpdest_map{std::move(map)}, + eof_version{version}, + code_offsets{std::move(offsets)} {} }; static_assert(std::is_move_constructible_v); diff --git a/lib/evmone/baseline_instruction_table.cpp b/lib/evmone/baseline_instruction_table.cpp index d46d22d765..2fd6b4b673 100644 --- a/lib/evmone/baseline_instruction_table.cpp +++ b/lib/evmone/baseline_instruction_table.cpp @@ -7,21 +7,46 @@ namespace evmone::baseline { -const CostTable& get_baseline_cost_table(evmc_revision rev) noexcept +namespace { - static constexpr auto cost_tables = []() noexcept { - std::array tables{}; - for (size_t r = EVMC_FRONTIER; r <= EVMC_MAX_REVISION; ++r) +constexpr auto common_cost_tables = []() noexcept { + std::array tables{}; + for (size_t r = EVMC_FRONTIER; r <= EVMC_MAX_REVISION; ++r) + { + auto& table = tables[r]; + for (size_t i = 0; i < table.size(); ++i) { - auto& table = tables[r]; - for (size_t i = 0; i < table.size(); ++i) - { - table[i] = instr::gas_costs[r][i]; // Include instr::undefined in the table. - } + table[i] = instr::gas_costs[r][i]; // Include instr::undefined in the table. } - return tables; - }(); + } + return tables; +}(); - return cost_tables[rev]; +constexpr auto legacy_cost_tables = []() noexcept { + auto tables = common_cost_tables; + tables[EVMC_CANCUN][OP_RJUMP] = instr::undefined; + tables[EVMC_CANCUN][OP_RJUMPI] = instr::undefined; + tables[EVMC_CANCUN][OP_RJUMPV] = instr::undefined; + tables[EVMC_CANCUN][OP_CALLF] = instr::undefined; + tables[EVMC_CANCUN][OP_RETF] = instr::undefined; + return tables; +}(); + +constexpr auto eof_cost_tables = []() noexcept { + auto tables = common_cost_tables; + tables[EVMC_CANCUN][OP_JUMP] = instr::undefined; + tables[EVMC_CANCUN][OP_JUMPI] = instr::undefined; + tables[EVMC_CANCUN][OP_PC] = instr::undefined; + tables[EVMC_CANCUN][OP_CALLCODE] = instr::undefined; + tables[EVMC_CANCUN][OP_SELFDESTRUCT] = instr::undefined; + return tables; +}(); + +} // namespace + +const CostTable& get_baseline_cost_table(evmc_revision rev, uint8_t eof_version) noexcept +{ + const auto& tables = (eof_version == 0) ? legacy_cost_tables : eof_cost_tables; + return tables[rev]; } } // namespace evmone::baseline diff --git a/lib/evmone/baseline_instruction_table.hpp b/lib/evmone/baseline_instruction_table.hpp index 7bdbd8b98f..f163bd9742 100644 --- a/lib/evmone/baseline_instruction_table.hpp +++ b/lib/evmone/baseline_instruction_table.hpp @@ -10,5 +10,7 @@ namespace evmone::baseline { using CostTable = std::array; -const CostTable& get_baseline_cost_table(evmc_revision rev) noexcept; +const CostTable& get_baseline_cost_table(evmc_revision rev, uint8_t eof_version) noexcept; + +const CostTable& get_baseline_legacy_cost_table(evmc_revision rev) noexcept; } // namespace evmone::baseline diff --git a/lib/evmone/eof.cpp b/lib/evmone/eof.cpp index 989e7a43dc..d6e8aa87db 100644 --- a/lib/evmone/eof.cpp +++ b/lib/evmone/eof.cpp @@ -3,12 +3,18 @@ // SPDX-License-Identifier: Apache-2.0 #include "eof.hpp" +#include "baseline_instruction_table.hpp" #include "instructions_traits.hpp" +#include #include #include #include +#include +#include +#include #include +#include namespace evmone { @@ -16,14 +22,37 @@ namespace { constexpr uint8_t MAGIC[] = {0xef, 0x00}; constexpr uint8_t TERMINATOR = 0x00; -constexpr uint8_t CODE_SECTION = 0x01; -constexpr uint8_t DATA_SECTION = 0x02; +constexpr uint8_t TYPE_SECTION = 0x01; +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 = 0x03FF; +constexpr auto OUTPUTS_INPUTS_NUMBER_LIMIT = 0x7F; +constexpr auto REL_OFFSET_SIZE = sizeof(int16_t); -using EOFSectionHeaders = std::array; +using EOFSectionHeaders = std::array, MAX_SECTION + 1>; -std::variant validate_eof_headers( - bytes_view container) noexcept +size_t eof_header_size(const EOFSectionHeaders& headers) noexcept +{ + const auto non_code_section_count = 2; // type section and data section + const auto code_section_count = headers[CODE_SECTION].size(); + + constexpr auto non_code_section_header_size = 3; // (SECTION_ID + SIZE) per each section + constexpr auto code_section_size_size = 2; + + return sizeof(MAGIC) + 1 + // 1 version byte + non_code_section_count * non_code_section_header_size + sizeof(CODE_SECTION) + 2 + + code_section_count * code_section_size_size + sizeof(TERMINATOR); +} + +EOFValidationError get_section_missing_error(uint8_t section_id) noexcept +{ + return static_cast( + static_cast(EOFValidationError::header_terminator_missing) + section_id); +} + +std::variant validate_eof_headers(bytes_view container) { enum class State { @@ -34,9 +63,11 @@ std::variant validate_eof_headers( auto state = State::section_id; uint8_t section_id = 0; + uint16_t section_num = 0; EOFSectionHeaders section_headers{}; const auto container_end = container.end(); auto it = container.begin() + std::size(MAGIC) + 1; // MAGIC + VERSION + uint8_t expected_section_id = TYPE_SECTION; while (it != container_end && state != State::terminated) { switch (state) @@ -44,41 +75,71 @@ std::variant validate_eof_headers( case State::section_id: { section_id = *it++; + + if (section_id != expected_section_id) + return get_section_missing_error(expected_section_id); + switch (section_id) { case TERMINATOR: - if (section_headers[CODE_SECTION] == 0) - return EOFValidationError::code_section_missing; state = State::terminated; break; - case DATA_SECTION: - if (section_headers[CODE_SECTION] == 0) - return EOFValidationError::code_section_missing; - if (section_headers[DATA_SECTION] != 0) - return EOFValidationError::multiple_data_sections; + case TYPE_SECTION: + expected_section_id = CODE_SECTION; state = State::section_size; break; case CODE_SECTION: - if (section_headers[CODE_SECTION] != 0) - return EOFValidationError::multiple_code_sections; + { + if (it >= container_end - 1) + return EOFValidationError::incomplete_section_number; + section_num = read_uint16_be(it); + it += 2; + if (section_num == 0) + return EOFValidationError::zero_section_size; + if (section_num > CODE_SECTION_NUMBER_LIMIT) + return EOFValidationError::too_many_code_sections; + expected_section_id = DATA_SECTION; + state = State::section_size; + break; + } + case DATA_SECTION: + expected_section_id = TERMINATOR; state = State::section_size; break; default: - return EOFValidationError::unknown_section_id; + assert(false); } break; } case State::section_size: { - const auto size_hi = *it++; - if (it == container_end) - return EOFValidationError::incomplete_section_size; - const auto size_lo = *it++; - const auto section_size = static_cast((size_hi << 8) | size_lo); - if (section_size == 0) - return EOFValidationError::zero_section_size; - - section_headers[section_id] = section_size; + if (section_id == CODE_SECTION) + { + assert(section_num > 0); // Guaranteed by previous validation step. + for (size_t i = 0; i < section_num; ++i) + { + if (it >= container_end - 1) + return EOFValidationError::incomplete_section_size; + const auto section_size = read_uint16_be(it); + it += 2; + if (section_size == 0) + return EOFValidationError::zero_section_size; + + section_headers[section_id].emplace_back(section_size); + } + } + else // TYPES_SECTION or DATA_SECTION + { + if (it >= container_end - 1) + return EOFValidationError::incomplete_section_size; + const auto section_size = read_uint16_be(it); + it += 2; + if (section_size == 0 && section_id != DATA_SECTION) + return EOFValidationError::zero_section_size; + + section_headers[section_id].emplace_back(section_size); + } + state = State::section_id; break; } @@ -90,37 +151,256 @@ std::variant validate_eof_headers( if (state != State::terminated) return EOFValidationError::section_headers_not_terminated; - const auto section_bodies_size = section_headers[CODE_SECTION] + section_headers[DATA_SECTION]; + const auto section_bodies_size = section_headers[TYPE_SECTION].front() + + std::accumulate(section_headers[CODE_SECTION].begin(), + section_headers[CODE_SECTION].end(), 0) + + section_headers[DATA_SECTION].front(); const auto remaining_container_size = container_end - it; if (section_bodies_size != remaining_container_size) return EOFValidationError::invalid_section_bodies_size; + if (section_headers[TYPE_SECTION][0] != section_headers[CODE_SECTION].size() * 4) + return EOFValidationError::invalid_type_section_size; + return section_headers; } +std::variant, EOFValidationError> validate_types( + bytes_view container, size_t header_size, uint16_t type_section_size) noexcept +{ + assert(!container.empty()); // guaranteed by EOF headers validation + + std::vector types; + + // guaranteed by EOF headers validation + assert(header_size + type_section_size < container.size()); + + for (auto offset = header_size; offset < header_size + type_section_size; offset += 4) + { + types.emplace_back( + container[offset], container[offset + 1], read_uint16_be(&container[offset + 2])); + } + + // check 1st section is (0, 0) + if (types[0].inputs != 0 || types[0].outputs != 0) + return EOFValidationError::invalid_first_section_type; + + for (const auto& t : types) + { + if (t.outputs > OUTPUTS_INPUTS_NUMBER_LIMIT || t.inputs > OUTPUTS_INPUTS_NUMBER_LIMIT) + return EOFValidationError::inputs_outputs_num_above_limit; + + if (t.max_stack_height > MAX_STACK_HEIGHT) + return EOFValidationError::max_stack_height_above_limit; + } + + return types; +} + EOFValidationError validate_instructions(evmc_revision rev, bytes_view code) noexcept { assert(!code.empty()); // guaranteed by EOF headers validation - size_t i = 0; - uint8_t op = code[0]; - while (i < code.size()) + const auto& cost_table = baseline::get_baseline_cost_table(rev, 1); + + for (size_t i = 0; i < code.size(); ++i) { - op = code[i]; - const auto& since = instr::traits[op].since; - if (!since.has_value() || *since > rev) + const auto op = code[i]; + if (cost_table[op] == instr::undefined) return EOFValidationError::undefined_instruction; - i += instr::traits[op].immediate_size; - ++i; - } + if (op == OP_RJUMPV) + { + if (i + 1 >= code.size()) + return EOFValidationError::truncated_instruction; - if (!instr::traits[op].is_terminating) - return EOFValidationError::missing_terminating_instruction; + const auto count = code[i + 1]; + if (count < 1) + return EOFValidationError::invalid_rjumpv_count; + i += static_cast(1 /* count */ + count * 2 /* tbl */); + } + else + i += instr::traits[op].immediate_size; + + if (i >= code.size()) + return EOFValidationError::truncated_instruction; + } return EOFValidationError::success; } +/// Validates that that we don't rjump inside an instruction's immediate. +/// Requires that the input is validated against truncation. +bool validate_rjump_destinations(bytes_view code) noexcept +{ + // Collect relative jump destinations and immediate locations + const auto code_size = code.size(); + // list of all possible absolute rjumps destinations positions + std::vector rjumpdests; + // bool map of immediate arguments positions in the code + std::vector immediate_map(code_size); + + /// Validates relative jump destination. If valid pushes the destination to the rjumpdests. + const auto check_rjump_destination = [code_size, &rjumpdests]( + auto rel_offset_data_it, size_t post_pos) -> bool { + const auto rel_offset = read_int16_be(rel_offset_data_it); + const auto jumpdest = static_cast(post_pos) + rel_offset; + if (jumpdest < 0 || static_cast(jumpdest) >= code_size) + return false; + + rjumpdests.emplace_back(static_cast(jumpdest)); + return true; + }; + + for (size_t i = 0; i < code_size; ++i) + { + const auto op = code[i]; + size_t imm_size = instr::traits[op].immediate_size; + + if (op == OP_RJUMP || op == OP_RJUMPI) + { + if (!check_rjump_destination(&code[i + 1], i + REL_OFFSET_SIZE + 1)) + return false; + } + else if (op == OP_RJUMPV) + { + const auto count = code[i + 1]; + imm_size += size_t{1} /* count */ + count * REL_OFFSET_SIZE /* tbl */; + const size_t post_pos = i + 1 + imm_size; + + for (size_t k = 0; k < count * REL_OFFSET_SIZE; k += REL_OFFSET_SIZE) + { + if (!check_rjump_destination(&code[i + 1 + 1 + static_cast(k)], post_pos)) + return false; + } + } + + std::fill_n(immediate_map.begin() + static_cast(i) + 1, imm_size, true); + i += imm_size; + } + + // Check relative jump destinations against immediate locations. + for (const auto rjumpdest : rjumpdests) + if (immediate_map[rjumpdest]) + return false; + + 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 { @@ -129,45 +409,104 @@ std::variant validate_eof1( return *error; const auto& section_headers = std::get(section_headers_or_error); - EOF1Header header{section_headers[CODE_SECTION], section_headers[DATA_SECTION]}; + const auto& code_sizes = section_headers[CODE_SECTION]; + const auto data_size = section_headers[DATA_SECTION][0]; - const auto error_instr = - validate_instructions(rev, {&container[header.code_begin()], header.code_size}); - if (error_instr != EOFValidationError::success) - return error_instr; + const auto header_size = eof_header_size(section_headers); - return header; -} + const auto types_or_error = + validate_types(container, header_size, section_headers[TYPE_SECTION].front()); + if (const auto* error = std::get_if(&types_or_error)) + return *error; + const auto& types = std::get>(types_or_error); -} // namespace + std::vector code_offsets; + const auto type_section_size = section_headers[TYPE_SECTION][0]; + auto offset = header_size + type_section_size; + for (const auto code_size : code_sizes) + { + assert(offset <= std::numeric_limits::max()); + code_offsets.emplace_back(static_cast(offset)); + offset += code_size; + } -size_t EOF1Header::code_begin() const noexcept -{ - assert(code_size != 0); + EOF1Header header{code_sizes, code_offsets, data_size, types}; - if (data_size == 0) - return 7; // MAGIC + VERSION + SECTION_ID + SIZE + TERMINATOR - else - return 10; // MAGIC + VERSION + SECTION_ID + SIZE + SECTION_ID + SIZE + TERMINATOR + for (size_t code_idx = 0; code_idx < header.code_sizes.size(); ++code_idx) + { + const auto error_instr = validate_instructions(rev, header.get_code(container, code_idx)); + if (error_instr != EOFValidationError::success) + return error_instr; + + 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; } +} // namespace + bool is_eof_container(bytes_view container) noexcept { return container.size() > 1 && container[0] == MAGIC[0] && container[1] == MAGIC[1]; } -EOF1Header read_valid_eof1_header(bytes_view container) noexcept +/// This function expects the prefix and version to be valid, as it ignores it. +EOF1Header read_valid_eof1_header(bytes_view container) { + EOFSectionHeaders section_headers; + auto it = container.begin() + std::size(MAGIC) + 1; // MAGIC + VERSION + while (*it != TERMINATOR) + { + const auto section_id = *it++; + if (section_id == CODE_SECTION) + { + const auto code_section_num = read_uint16_be(it); + it += 2; + for (uint16_t i = 0; i < code_section_num; ++i) + { + const auto section_size = read_uint16_be(it); + it += 2; + section_headers[section_id].emplace_back(section_size); + } + } + else + { + const auto section_size = read_uint16_be(it); + it += 2; + section_headers[section_id].emplace_back(section_size); + } + } + const auto header_size = eof_header_size(section_headers); + EOF1Header header; - const auto code_size_offset = 4; // MAGIC + VERSION + CODE_SECTION_ID - header.code_size = - static_cast((container[code_size_offset] << 8) | container[code_size_offset + 1]); - if (container[code_size_offset + 2] == 2) // is data section present + + for (auto type_offset = header_size; + type_offset < header_size + section_headers[TYPE_SECTION][0]; type_offset += 4) + { + header.types.emplace_back(container[type_offset], container[type_offset + 1], + read_uint16_be(&container[type_offset + 2])); + } + + header.code_sizes = section_headers[CODE_SECTION]; + auto code_offset = header_size + section_headers[TYPE_SECTION][0]; + for (const auto code_size : header.code_sizes) { - const auto data_size_offset = code_size_offset + 3; - header.data_size = static_cast( - (container[data_size_offset] << 8) | container[data_size_offset + 1]); + assert(code_offset <= std::numeric_limits::max()); + header.code_offsets.emplace_back(static_cast(code_offset)); + code_offset += code_size; } + + header.data_size = section_headers[DATA_SECTION][0]; + return header; } @@ -216,14 +555,16 @@ std::string_view get_error_message(EOFValidationError err) noexcept return "eof_version_unknown"; case EOFValidationError::incomplete_section_size: return "incomplete_section_size"; + case EOFValidationError::incomplete_section_number: + return "incomplete_section_number"; + case EOFValidationError::header_terminator_missing: + return "header_terminator_missing"; + case EOFValidationError::type_section_missing: + return "type_section_missing"; case EOFValidationError::code_section_missing: return "code_section_missing"; - case EOFValidationError::multiple_code_sections: - return "multiple_code_sections"; - case EOFValidationError::multiple_data_sections: - return "multiple_data_sections"; - case EOFValidationError::unknown_section_id: - return "unknown_section_id"; + case EOFValidationError::data_section_missing: + return "data_section_missing"; case EOFValidationError::zero_section_size: return "zero_section_size"; case EOFValidationError::section_headers_not_terminated: @@ -232,8 +573,36 @@ std::string_view get_error_message(EOFValidationError err) noexcept return "invalid_section_bodies_size"; case EOFValidationError::undefined_instruction: return "undefined_instruction"; - case EOFValidationError::missing_terminating_instruction: - return "missing_terminating_instruction"; + case EOFValidationError::truncated_instruction: + return "truncated_instruction"; + case EOFValidationError::invalid_rjumpv_count: + return "invalid_rjumpv_count"; + case EOFValidationError::invalid_rjump_destination: + return "invalid_rjump_destination"; + case EOFValidationError::too_many_code_sections: + return "too_many_code_sections"; + case EOFValidationError::invalid_type_section_size: + return "invalid_type_section_size"; + case EOFValidationError::invalid_first_section_type: + return "invalid_first_section_type"; + case EOFValidationError::invalid_max_stack_height: + return "invalid_max_stack_height"; + case EOFValidationError::max_stack_height_above_limit: + 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 ece2444d0b..75febe7fdc 100644 --- a/lib/evmone/eof.hpp +++ b/lib/evmone/eof.hpp @@ -5,21 +5,45 @@ #include #include +#include #include #include #include +#include namespace evmone { using bytes_view = std::basic_string_view; +struct EOFCodeType +{ + uint8_t inputs; ///< Number of code inputs. + uint8_t outputs; ///< Number of code outputs. + uint16_t max_stack_height; ///< Maximum stack height reached in the code. + + EOFCodeType(uint8_t inputs_, uint8_t outputs_, uint16_t max_stack_height_) + : inputs{inputs_}, outputs{outputs_}, max_stack_height{max_stack_height_} + {} +}; + struct EOF1Header { - uint16_t code_size = 0; + /// Size of every code section. + std::vector code_sizes; + + /// Offset of every code section from the beginning of the EOF container. + std::vector code_offsets; + uint16_t data_size = 0; - /// Returns offset of code section start. - [[nodiscard]] EVMC_EXPORT size_t code_begin() const noexcept; + std::vector types; + + /// A helper to extract reference to a specific code section. + [[nodiscard]] bytes_view get_code(bytes_view container, size_t code_idx) const noexcept + { + assert(code_idx < code_offsets.size()); + return container.substr(code_offsets[code_idx], code_sizes[code_idx]); + } }; /// Checks if code starts with EOF FORMAT + MAGIC, doesn't validate the format. @@ -27,7 +51,7 @@ struct EOF1Header /// Reads the section sizes assuming that container has valid format. /// (must be true for all EOF contracts on-chain) -[[nodiscard]] EVMC_EXPORT EOF1Header read_valid_eof1_header(bytes_view container) noexcept; +[[nodiscard]] EVMC_EXPORT EOF1Header read_valid_eof1_header(bytes_view container); enum class EOFValidationError { @@ -38,15 +62,30 @@ enum class EOFValidationError eof_version_unknown, incomplete_section_size, + incomplete_section_number, + header_terminator_missing, + type_section_missing, code_section_missing, - multiple_code_sections, - multiple_data_sections, - unknown_section_id, + data_section_missing, zero_section_size, section_headers_not_terminated, invalid_section_bodies_size, undefined_instruction, - missing_terminating_instruction, + truncated_instruction, + invalid_rjumpv_count, + invalid_rjump_destination, + too_many_code_sections, + 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, }; @@ -61,4 +100,23 @@ enum class EOFValidationError /// Returns the error message corresponding to an error code. [[nodiscard]] EVMC_EXPORT std::string_view get_error_message(EOFValidationError err) noexcept; + +/// Loads big endian int16_t from data. Unsafe. +/// TODO: Move it to intx +inline int16_t read_int16_be(auto it) noexcept +{ + const uint8_t h = *it++; + const uint8_t l = *it; + return static_cast((h << 8) | l); +} + +/// Loads big endian uint16_t from data. Unsafe. +/// TODO: Move it to intx +inline uint16_t read_uint16_be(auto it) noexcept +{ + const uint8_t h = *it++; + const uint8_t l = *it; + return static_cast((h << 8) | l); +} + } // namespace evmone diff --git a/lib/evmone/execution_state.hpp b/lib/evmone/execution_state.hpp index c212da8809..8aa54353ca 100644 --- a/lib/evmone/execution_state.hpp +++ b/lib/evmone/execution_state.hpp @@ -154,6 +154,8 @@ class ExecutionState const advanced::AdvancedCodeAnalysis* advanced; } analysis{}; + std::vector call_stack; + /// Stack space allocation. /// /// This is the last field to make other fields' offsets of reasonable values. diff --git a/lib/evmone/instructions.hpp b/lib/evmone/instructions.hpp index e525387d83..6a95e48662 100644 --- a/lib/evmone/instructions.hpp +++ b/lib/evmone/instructions.hpp @@ -4,6 +4,7 @@ #pragma once #include "baseline.hpp" +#include "eof.hpp" #include "execution_state.hpp" #include "instructions_traits.hpp" #include "instructions_xmacro.hpp" @@ -701,6 +702,40 @@ inline code_iterator jumpi(StackTop stack, ExecutionState& state, code_iterator return cond ? jump_impl(state, dst) : pos + 1; } +inline code_iterator rjump(StackTop /*stack*/, ExecutionState& /*state*/, code_iterator pc) noexcept +{ + // Reading next 2 bytes is guaranteed to be safe by deploy-time validation. + const auto offset = read_int16_be(&pc[1]); + return pc + 3 + offset; // PC_post_rjump + offset +} + +inline code_iterator rjumpi(StackTop stack, ExecutionState& state, code_iterator pc) noexcept +{ + const auto cond = stack.pop(); + return cond ? rjump(stack, state, pc) : pc + 3; +} + +inline code_iterator rjumpv(StackTop stack, ExecutionState& /*state*/, code_iterator pc) noexcept +{ + constexpr auto REL_OFFSET_SIZE = sizeof(int16_t); + const auto case_ = stack.pop(); + + const auto count = pc[1]; + const auto pc_post = pc + 1 + 1 /* count */ + count * REL_OFFSET_SIZE /* tbl */; + + if (case_ >= count) + { + return pc_post; + } + else + { + const auto rel_offset = + read_int16_be(&pc[2 + static_cast(case_) * REL_OFFSET_SIZE]); + + return pc_post + rel_offset; + } +} + inline code_iterator pc(StackTop stack, ExecutionState& state, code_iterator pos) noexcept { stack.push(static_cast(pos - state.analysis.baseline->executable_code.data())); @@ -898,6 +933,22 @@ evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept; inline constexpr auto create = create_impl; inline constexpr auto create2 = create_impl; +inline code_iterator callf(StackTop /*stack*/, ExecutionState& state, code_iterator pos) noexcept +{ + const auto index = read_uint16_be(&pos[1]); + state.call_stack.push_back(pos + 3); + const auto offset = state.analysis.baseline->code_offsets[index]; + auto code = state.analysis.baseline->executable_code; + return code.data() + offset; +} + +inline code_iterator retf(StackTop /*stack*/, ExecutionState& state, code_iterator /*pos*/) noexcept +{ + const auto p = state.call_stack.back(); + state.call_stack.pop_back(); + return p; +} + template inline StopToken return_impl(StackTop stack, ExecutionState& state) noexcept { diff --git a/lib/evmone/instructions_opcodes.hpp b/lib/evmone/instructions_opcodes.hpp index fdeb97e843..9b430ce47a 100644 --- a/lib/evmone/instructions_opcodes.hpp +++ b/lib/evmone/instructions_opcodes.hpp @@ -82,6 +82,9 @@ enum Opcode : uint8_t OP_MSIZE = 0x59, OP_GAS = 0x5a, OP_JUMPDEST = 0x5b, + OP_RJUMP = 0x5c, + OP_RJUMPI = 0x5d, + OP_RJUMPV = 0x5e, OP_PUSH0 = 0x5f, OP_PUSH1 = 0x60, @@ -154,6 +157,9 @@ enum Opcode : uint8_t OP_LOG3 = 0xa3, OP_LOG4 = 0xa4, + OP_CALLF = 0xb0, + OP_RETF = 0xb1, + OP_DUPN = 0xb5, OP_SWAPN = 0xb6, diff --git a/lib/evmone/instructions_traits.hpp b/lib/evmone/instructions_traits.hpp index 8d6810d847..fc4bb85776 100644 --- a/lib/evmone/instructions_traits.hpp +++ b/lib/evmone/instructions_traits.hpp @@ -166,6 +166,11 @@ constexpr inline GasCostTable gas_costs = []() noexcept { table[EVMC_CANCUN] = table[EVMC_SHANGHAI]; table[EVMC_CANCUN][OP_DUPN] = 3; table[EVMC_CANCUN][OP_SWAPN] = 3; + table[EVMC_CANCUN][OP_RJUMP] = 2; + table[EVMC_CANCUN][OP_RJUMPI] = 4; + table[EVMC_CANCUN][OP_RJUMPV] = 4; + table[EVMC_CANCUN][OP_CALLF] = 5; + table[EVMC_CANCUN][OP_RETF] = 3; table[EVMC_PRAGUE] = table[EVMC_CANCUN]; @@ -288,6 +293,10 @@ constexpr inline std::array traits = []() noexcept { table[OP_MSIZE] = {"MSIZE", 0, false, 0, 1, EVMC_FRONTIER}; table[OP_GAS] = {"GAS", 0, false, 0, 1, EVMC_FRONTIER}; table[OP_JUMPDEST] = {"JUMPDEST", 0, false, 0, 0, EVMC_FRONTIER}; + table[OP_RJUMP] = {"RJUMP", 2, false, 0, 0, EVMC_CANCUN}; + table[OP_RJUMPI] = {"RJUMPI", 2, false, 1, -1, EVMC_CANCUN}; + table[OP_RJUMPV] = { + "RJUMPV", 0 /* WARNING: immediate_size is dynamic */, false, 1, -1, EVMC_CANCUN}; table[OP_PUSH0] = {"PUSH0", 0, false, 0, 1, EVMC_SHANGHAI}; @@ -374,6 +383,8 @@ constexpr inline std::array traits = []() noexcept { table[OP_DELEGATECALL] = {"DELEGATECALL", 0, false, 6, -5, EVMC_HOMESTEAD}; table[OP_CREATE2] = {"CREATE2", 0, false, 4, -3, EVMC_CONSTANTINOPLE}; table[OP_STATICCALL] = {"STATICCALL", 0, false, 6, -5, EVMC_BYZANTIUM}; + table[OP_CALLF] = {"CALLF", 2, false, 0, 0, EVMC_CANCUN}; + table[OP_RETF] = {"RETF", 0, true, 0, 0, EVMC_CANCUN}; table[OP_REVERT] = {"REVERT", 0, true, 2, -2, EVMC_BYZANTIUM}; table[OP_INVALID] = {"INVALID", 0, true, 0, 0, EVMC_FRONTIER}; table[OP_SELFDESTRUCT] = {"SELFDESTRUCT", 0, true, 1, -1, EVMC_FRONTIER}; diff --git a/lib/evmone/instructions_xmacro.hpp b/lib/evmone/instructions_xmacro.hpp index d69b5c13f5..7c81d7f135 100644 --- a/lib/evmone/instructions_xmacro.hpp +++ b/lib/evmone/instructions_xmacro.hpp @@ -129,9 +129,9 @@ ON_OPCODE_IDENTIFIER(OP_MSIZE, msize) \ ON_OPCODE_IDENTIFIER(OP_GAS, gas) \ ON_OPCODE_IDENTIFIER(OP_JUMPDEST, jumpdest) \ - ON_OPCODE_UNDEFINED(0x5c) \ - ON_OPCODE_UNDEFINED(0x5d) \ - ON_OPCODE_UNDEFINED(0x5e) \ + ON_OPCODE_IDENTIFIER(OP_RJUMP, rjump) \ + ON_OPCODE_IDENTIFIER(OP_RJUMPI, rjumpi) \ + ON_OPCODE_IDENTIFIER(OP_RJUMPV, rjumpv) \ ON_OPCODE_IDENTIFIER(OP_PUSH0, push0) \ \ ON_OPCODE_IDENTIFIER(OP_PUSH1, push<1>) \ @@ -219,8 +219,8 @@ ON_OPCODE_UNDEFINED(0xae) \ ON_OPCODE_UNDEFINED(0xaf) \ \ - ON_OPCODE_UNDEFINED(0xb0) \ - ON_OPCODE_UNDEFINED(0xb1) \ + ON_OPCODE_IDENTIFIER(OP_CALLF, callf) \ + ON_OPCODE_IDENTIFIER(OP_RETF, retf) \ ON_OPCODE_UNDEFINED(0xb2) \ ON_OPCODE_UNDEFINED(0xb3) \ ON_OPCODE_UNDEFINED(0xb4) \ diff --git a/test/eofparse/eofparse.cpp b/test/eofparse/eofparse.cpp index 118dc21c5b..6fd9937bbc 100644 --- a/test/eofparse/eofparse.cpp +++ b/test/eofparse/eofparse.cpp @@ -60,7 +60,14 @@ int main() } const auto header = evmone::read_valid_eof1_header(eof); - std::cout << "OK " << evmc::hex({&eof[header.code_begin()], header.code_size}) << "\n"; + std::cout << "OK "; + for (size_t i = 0; i < header.code_sizes.size(); ++i) + { + if (i != 0) + std::cout << ","; + std::cout << evmc::hex(header.get_code(eof, i)); + } + std::cout << "\n"; } return 0; } diff --git a/test/state/host.cpp b/test/state/host.cpp index ae55ca5457..6d254fa1d9 100644 --- a/test/state/host.cpp +++ b/test/state/host.cpp @@ -5,6 +5,7 @@ #include "host.hpp" #include "precompiles.hpp" #include "rlp.hpp" +#include namespace evmone::state { @@ -194,9 +195,16 @@ evmc::Result Host::create(const evmc_message& msg) noexcept new_acc.balance += value; // The new account may be prefunded. auto create_msg = msg; + const bytes_view initcode{msg.input_data, msg.input_size}; create_msg.input_data = nullptr; create_msg.input_size = 0; + if (m_rev >= EVMC_CANCUN && (is_eof_container(initcode) || is_eof_container(sender_acc.code))) + { + if (validate_eof(m_rev, initcode) != EOFValidationError::success) + return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE}; + } + auto result = m_vm.execute(*this, m_rev, create_msg, msg.input_data, msg.input_size); if (result.status_code != EVMC_SUCCESS) { @@ -220,8 +228,12 @@ evmc::Result Host::create(const evmc_message& msg) noexcept evmc::Result{EVMC_FAILURE}; } - // Reject EF code. - if (m_rev >= EVMC_LONDON && !code.empty() && code[0] == 0xEF) + if (m_rev >= EVMC_CANCUN && (is_eof_container(initcode) || is_eof_container(code))) + { + if (validate_eof(m_rev, code) != EOFValidationError::success) + return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE}; + } + else if (m_rev >= EVMC_LONDON && !code.empty() && code[0] == 0xEF) // Reject EF code. return evmc::Result{EVMC_CONTRACT_VALIDATION_FAILURE}; // TODO: The new_acc pointer is invalid because of the state revert implementation, diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index e1f597c29e..9f9d5d62e0 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -21,6 +21,8 @@ target_sources( evm_eip3855_push0_test.cpp evm_eip3860_initcode_test.cpp evm_eof_test.cpp + evm_eof_function_test.cpp + evm_eof_rjump_test.cpp evm_memory_test.cpp evm_state_test.cpp evm_storage_test.cpp diff --git a/test/unittests/analysis_test.cpp b/test/unittests/analysis_test.cpp index c2f8ee938f..a7b2661c3c 100644 --- a/test/unittests/analysis_test.cpp +++ b/test/unittests/analysis_test.cpp @@ -258,9 +258,9 @@ 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, {&code[header.code_begin()], header.code_size}); + const auto analysis = analyze(EVMC_CANCUN, header.get_code(code, 0)); ASSERT_EQ(analysis.instrs.size(), 8); diff --git a/test/unittests/eof_test.cpp b/test/unittests/eof_test.cpp index d82205fbce..673364318e 100644 --- a/test/unittests/eof_test.cpp +++ b/test/unittests/eof_test.cpp @@ -8,21 +8,6 @@ using namespace evmone; -TEST(eof, code_begin) -{ - const EOF1Header header1{1, 0}; - EXPECT_EQ(header1.code_begin(), 7); - - const EOF1Header header2{10, 0}; - EXPECT_EQ(header2.code_begin(), 7); - - const EOF1Header header3{1, 1}; - EXPECT_EQ(header3.code_begin(), 10); - - const EOF1Header header4{1, 10}; - EXPECT_EQ(header4.code_begin(), 10); -} - TEST(eof, is_eof_container) { EXPECT_FALSE(is_eof_container(""_hex)); @@ -34,8 +19,8 @@ TEST(eof, is_eof_container) EXPECT_FALSE(is_eof_container("FE"_hex)); EXPECT_TRUE(is_eof_container("EF00"_hex)); - EXPECT_TRUE(is_eof_container("EF00 01 010001 00 00"_hex)); - EXPECT_TRUE(is_eof_container("EF00 01 010001 020004 00 00 AABBCCDD"_hex)); + EXPECT_TRUE(is_eof_container("EF00 01 010004 0200010001 00 00000000 00"_hex)); + EXPECT_TRUE(is_eof_container("EF00 01 010004 0200010001 030004 00 00000000 00 AABBCCDD"_hex)); EXPECT_TRUE(is_eof_container("EF00 02 ABCFEF"_hex)); } @@ -44,22 +29,26 @@ TEST(eof, read_valid_eof1_header) struct TestCase { std::string code; + uint16_t types_size; uint16_t code_size; uint16_t data_size; }; const TestCase test_cases[] = { - {"EF00 01 010001 00 00", 1, 0}, - {"EF00 01 010006 00 600160005500", 6, 0}, - {"EF00 01 010001 020001 00 00 00 AA", 1, 1}, - {"EF00 01 010006 020004 00 600160005500 AABBCCDD", 6, 4}, - {"EF00 01 010100 021000 00" + std::string(256, '0') + std::string(4096, 'F'), 256, 4096}, + {"EF00 01 010004 0200010001 030000 00 00000000 00", 4, 1, 0}, + {"EF00 01 010004 0200010006 030000 00 00000400 600160005500", 4, 6, 0}, + {"EF00 01 010004 0200010001 030001 00 00000000 00 00 AA", 4, 1, 1}, + {"EF00 01 010004 0200010006 030004 00 00000000 600160005500 AABBCCDD", 4, 6, 4}, + {"EF00 01 010004 0200010100 031000 00 00000000" + std::string(256, '0') + + std::string(4096, 'F'), + 4, 256, 4096}, }; for (const auto& test_case : test_cases) { const auto code = from_spaced_hex(test_case.code).value(); const auto header = read_valid_eof1_header(code); - EXPECT_EQ(header.code_size, test_case.code_size) << test_case.code; + EXPECT_EQ(header.code_sizes[0], test_case.code_size) << test_case.code; EXPECT_EQ(header.data_size, test_case.data_size) << test_case.code; + EXPECT_EQ(header.types.size() * 4, test_case.types_size) << test_case.code; } } diff --git a/test/unittests/eof_validation_test.cpp b/test/unittests/eof_validation_test.cpp index 755887a236..39a5dcf41d 100644 --- a/test/unittests/eof_validation_test.cpp +++ b/test/unittests/eof_validation_test.cpp @@ -48,7 +48,7 @@ TEST(eof_validation, validate_EOF_prefix) EXPECT_EQ(validate_eof("EF0001"), EOFValidationError::section_headers_not_terminated); // valid except for magic - EXPECT_EQ(validate_eof("EFFF01 010003 020004 00 600000 AABBCCDD"), + EXPECT_EQ(validate_eof("EFFF 01 010004 0200010003 030004 00 00000000 600000 AABBCCDD"), EOFValidationError::invalid_prefix); } @@ -58,145 +58,315 @@ TEST(eof_validation, validate_EOF_version) EXPECT_EQ(validate_eof("EF00FF"), EOFValidationError::eof_version_unknown); // valid except version - EXPECT_EQ(validate_eof("EF00000 10003 020004 00 600000 AABBCCDD"), + EXPECT_EQ(validate_eof("EF0000 010004 0200010003 020004 00 00000000 600000 AABBCCDD"), EOFValidationError::eof_version_unknown); - EXPECT_EQ(validate_eof("EF00020 10003 020004 00 600000 AABBCCDD"), + EXPECT_EQ(validate_eof("EF0002 010004 0200010003 020004 00 00000000 600000 AABBCCDD"), EOFValidationError::eof_version_unknown); - EXPECT_EQ(validate_eof("EF00FF0 10003 020004 00 600000 AABBCCDD"), + EXPECT_EQ(validate_eof("EF00FF 010004 0200010003 020004 00 00000000 600000 AABBCCDD"), EOFValidationError::eof_version_unknown); } TEST(eof_validation, valid_EOF1_code_pre_shanghai) { - EXPECT_EQ( - validate_eof("EF0001 010001 00 FE", EVMC_PARIS), EOFValidationError::eof_version_unknown); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 00 00000000 FE", EVMC_PARIS), + EOFValidationError::eof_version_unknown); } TEST(eof_validation, minimal_valid_EOF1_code) { - EXPECT_EQ(validate_eof("EF0001 010001 00 FE"), EOFValidationError::success); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030000 00 00000000 FE"), + EOFValidationError::success); } TEST(eof_validation, minimal_valid_EOF1_code_with_data) { - EXPECT_EQ(validate_eof("EF0001 010001 020001 00 FE DA"), EOFValidationError::success); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001 00 00000000 FE DA"), + EOFValidationError::success); +} + +TEST(eof_validation, minimal_valid_EOF1_multiple_code_sections) +{ + // no data section + EXPECT_EQ(validate_eof("EF0001 010008 02000200010001 00 00000000 00000000 FE FE"), + EOFValidationError::data_section_missing); + // with data section + EXPECT_EQ(validate_eof("EF0001 010008 02000200010001 030001 00 00000000 00000000 FE FE DA"), + EOFValidationError::success); + + // non-void input and output types + EXPECT_EQ(validate_eof("EF0001 010010 0200040001000200020002 030000 00 " + "00000000 01000001 00010001 02030003" + "FE 5000 3000 8000"), + EOFValidationError::success); +} + +TEST(eof_validation, EOF1_types_section_missing) +{ + EXPECT_EQ(validate_eof("EF0001 0200010001 00 FE"), EOFValidationError::type_section_missing); + EXPECT_EQ(validate_eof("EF0001 0200010001 030001 00 FE DA"), + EOFValidationError::type_section_missing); +} + +TEST(eof_validation, EOF1_types_section_0_size) +{ + EXPECT_EQ( + validate_eof("EF0001 010000 0200010001 00 FE"), EOFValidationError::zero_section_size); + EXPECT_EQ(validate_eof("EF0001 010000 0200010001 030001 00 FE DA"), + EOFValidationError::zero_section_size); +} + +TEST(eof_validation, EOF1_type_section_missing) +{ + EXPECT_EQ(validate_eof("EF0001 00"), EOFValidationError::type_section_missing); } TEST(eof_validation, EOF1_code_section_missing) { - EXPECT_EQ(validate_eof("EF0001 00"), EOFValidationError::code_section_missing); - EXPECT_EQ(validate_eof("EF0001 020001 DA"), EOFValidationError::code_section_missing); + EXPECT_EQ(validate_eof("EF0001 010004 00"), EOFValidationError::code_section_missing); + EXPECT_EQ(validate_eof("EF0001 010004 030001 00 00000000 DA"), + EOFValidationError::code_section_missing); } TEST(eof_validation, EOF1_code_section_0_size) { - EXPECT_EQ(validate_eof("EF0001 010000 00"), EOFValidationError::zero_section_size); - EXPECT_EQ(validate_eof("EF0001 010000 020001 00 DA"), EOFValidationError::zero_section_size); + EXPECT_EQ(validate_eof("EF0001 010004 020000 00"), EOFValidationError::zero_section_size); + EXPECT_EQ( + validate_eof("EF0001 010004 020000 030001 00 DA"), EOFValidationError::zero_section_size); } TEST(eof_validation, EOF1_data_section_0_size) { - EXPECT_EQ(validate_eof("EF0001 010001 020000 00 FE"), EOFValidationError::zero_section_size); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030000 00 00000000 FE"), + EOFValidationError::success); } -TEST(eof_validation, EOF1_multiple_code_sections) +TEST(eof_validation, EOF1_data_section_before_code_section) { - EXPECT_EQ( - validate_eof("EF0001 010001 010001 00 FE FE"), EOFValidationError::multiple_code_sections); - EXPECT_EQ(validate_eof("EF0001 010001 010001 020001 00 FE FE DA"), - EOFValidationError::multiple_code_sections); + EXPECT_EQ(validate_eof("EF0001 010004 030001 0200010001 00 00000000 AA FE"), + EOFValidationError::code_section_missing); } -TEST(eof_validation, EOF1_data_section_before_code_section) +TEST(eof_validation, EOF1_data_section_before_types_section) { - EXPECT_EQ( - validate_eof("EF0001 020001 010001 00 AA FE"), EOFValidationError::code_section_missing); + EXPECT_EQ(validate_eof("EF0001 030001 010004 0200010001 00 AA 00000000 FE"), + EOFValidationError::type_section_missing); } TEST(eof_validation, EOF1_multiple_data_sections) { - EXPECT_EQ(validate_eof("EF0001 010001 020001 020001 00 FE DA DA"), - EOFValidationError::multiple_data_sections); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001 030001 00 00000000 FE DA DA"), + EOFValidationError::header_terminator_missing); } TEST(eof_validation, EOF1_unknown_section) { - EXPECT_EQ(validate_eof("EF0001 030001 00 FE"), EOFValidationError::unknown_section_id); - EXPECT_EQ(validate_eof("EF0001 FF0001 00 FE"), EOFValidationError::unknown_section_id); - EXPECT_EQ( - validate_eof("EF0001 010001 030001 00 FE 00"), EOFValidationError::unknown_section_id); - EXPECT_EQ( - validate_eof("EF0001 010001 FF0001 00 FE 00"), EOFValidationError::unknown_section_id); - EXPECT_EQ(validate_eof("EF0001 010001 020001 030001 00 FE AA 00"), - EOFValidationError::unknown_section_id); - EXPECT_EQ(validate_eof("EF0001 010001 020001 FF0001 00 FE AA 00"), - EOFValidationError::unknown_section_id); + EXPECT_EQ(validate_eof("EF0001 040001 00 FE"), EOFValidationError::type_section_missing); + EXPECT_EQ(validate_eof("EF0001 FF0001 00 FE"), EOFValidationError::type_section_missing); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 040001 00 00000000 FE 00"), + EOFValidationError::data_section_missing); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 FF0001 00 00000000 FE 00"), + EOFValidationError::data_section_missing); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001 040001 00 00000000 FE AA 00"), + EOFValidationError::header_terminator_missing); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001 FF0001 00 00000000 FE AA 00"), + EOFValidationError::header_terminator_missing); } TEST(eof_validation, EOF1_incomplete_section_size) { + // TODO: section_headers_not_terminated should rather be incomplete_section_size + // in these examples. + + EXPECT_EQ(validate_eof("EF0001 01"), EOFValidationError::section_headers_not_terminated); EXPECT_EQ(validate_eof("EF0001 0100"), EOFValidationError::incomplete_section_size); - EXPECT_EQ(validate_eof("EF0001 010001 0200"), EOFValidationError::incomplete_section_size); + EXPECT_EQ(validate_eof("EF0001 010004 0200"), EOFValidationError::incomplete_section_number); + EXPECT_EQ(validate_eof("EF0001 010004 02000100"), EOFValidationError::incomplete_section_size); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001"), + EOFValidationError::section_headers_not_terminated); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 03"), + EOFValidationError::section_headers_not_terminated); + EXPECT_EQ( + validate_eof("EF0001 010004 0200010001 0300"), EOFValidationError::incomplete_section_size); } TEST(eof_validation, EOF1_header_not_terminated) { EXPECT_EQ(validate_eof("EF0001 01"), EOFValidationError::section_headers_not_terminated); - EXPECT_EQ(validate_eof("EF0001 010001"), EOFValidationError::section_headers_not_terminated); - EXPECT_EQ(validate_eof("EF0001 010001 FE"), EOFValidationError::unknown_section_id); - EXPECT_EQ(validate_eof("EF0001 010001 02"), EOFValidationError::section_headers_not_terminated); + EXPECT_EQ(validate_eof("EF0001 010004"), EOFValidationError::section_headers_not_terminated); + EXPECT_EQ(validate_eof("EF0001 010004 FE"), EOFValidationError::code_section_missing); + EXPECT_EQ(validate_eof("EF0001 010004 02"), EOFValidationError::incomplete_section_number); + EXPECT_EQ(validate_eof("EF0001 010004 0200"), EOFValidationError::incomplete_section_number); EXPECT_EQ( - validate_eof("EF0001 010001 020001"), EOFValidationError::section_headers_not_terminated); - EXPECT_EQ(validate_eof("EF0001 010001 020001 FE AA"), EOFValidationError::unknown_section_id); + validate_eof("EF0001 010004 020001"), EOFValidationError::section_headers_not_terminated); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001"), + EOFValidationError::section_headers_not_terminated); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030001 FE AA"), + EOFValidationError::header_terminator_missing); } TEST(eof_validation, EOF1_truncated_section) { - EXPECT_EQ(validate_eof("EF0001 010002 00"), EOFValidationError::invalid_section_bodies_size); - EXPECT_EQ(validate_eof("EF0001 010002 00 FE"), EOFValidationError::invalid_section_bodies_size); - EXPECT_EQ(validate_eof("EF0001 010001 020002 00 FE"), + EXPECT_EQ(validate_eof("EF0001 010004 0200010002 030000 00"), + EOFValidationError::invalid_section_bodies_size); + EXPECT_EQ(validate_eof("EF0001 010004 0200010002 030000 00 000000"), + EOFValidationError::invalid_section_bodies_size); + EXPECT_EQ(validate_eof("EF0001 010004 0200010002 030000 00 00000000 FE"), + EOFValidationError::invalid_section_bodies_size); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030002 00 00000000 FE"), EOFValidationError::invalid_section_bodies_size); - EXPECT_EQ(validate_eof("EF0001 010001 020002 00 FE AA"), + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030002 00 00000000 FE AA"), EOFValidationError::invalid_section_bodies_size); } +TEST(eof_validation, EOF1_code_section_offset) +{ + const auto eof = + "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], 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], 28); +} + TEST(eof_validation, EOF1_trailing_bytes) { - EXPECT_EQ(validate_eof("EF0001 010001 00 FE DEADBEEF"), + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030000 00 00000000 FE DEADBEEF"), EOFValidationError::invalid_section_bodies_size); - EXPECT_EQ(validate_eof("EF0001 010001 020002 00 FE AABB DEADBEEF"), + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030002 00 00000000 FE AABB DEADBEEF"), EOFValidationError::invalid_section_bodies_size); } -TEST(eof_validation, EOF1_undefined_opcodes) +TEST(eof_validation, EOF1_no_type_section) +{ + EXPECT_EQ(validate_eof("EF0001 0200010001 00 FE"), EOFValidationError::type_section_missing); + EXPECT_EQ( + validate_eof("EF0001 02000200010001 00 FE FE"), EOFValidationError::type_section_missing); +} + +TEST(eof_validation, EOF1_multiple_type_sections) +{ + EXPECT_EQ(validate_eof("EF0001 010004 010004 02000200010001 00 00000000 00000000 FE FE"), + EOFValidationError::code_section_missing); + + // Section order is must be (Types, Code+, Data) + EXPECT_EQ(validate_eof("EF0001 030002 010001 010001 030002 00 0000 FE FE 0000"), + EOFValidationError::type_section_missing); +} + +TEST(eof_validation, EOF1_type_section_not_first) +{ + EXPECT_EQ(validate_eof("EF0001 0200010001 010004 00 FE 00000000"), + EOFValidationError::type_section_missing); + + EXPECT_EQ(validate_eof("EF0001 02000200010001 010004 00 FE FE 00000000"), + EOFValidationError::type_section_missing); + + EXPECT_EQ(validate_eof("EF0001 0200010001 010004 030003 00 FE 00000000 AABBCC"), + EOFValidationError::type_section_missing); + + EXPECT_EQ(validate_eof("EF0001 0200010001 030003 010004 00 FE AABBCC 00000000"), + EOFValidationError::type_section_missing); +} + +TEST(eof_validation, EOF1_invalid_type_section_size) +{ + EXPECT_EQ(validate_eof("EF0001 010001 0200010001 030000 00 00 FE"), + EOFValidationError::invalid_type_section_size); + EXPECT_EQ(validate_eof("EF0001 010002 0200010001 030000 00 0000 FE"), + EOFValidationError::invalid_type_section_size); + EXPECT_EQ(validate_eof("EF0001 010008 0200010001 030000 00 0000000000000000 FE"), + EOFValidationError::invalid_type_section_size); + + EXPECT_EQ(validate_eof("EF0001 010008 020003000100010001 030000 00 0000000000000000 FE FE FE"), + EOFValidationError::invalid_type_section_size); + EXPECT_EQ( + validate_eof( + "EF0001 010010 020003000100010001 030000 00 00000000000000000000000000000000 FE FE FE"), + EOFValidationError::invalid_type_section_size); +} + +TEST(eof_validation, EOF1_invalid_section_0_type) { - auto cont = "EF0001 010002 00 0000"_hex; + EXPECT_EQ(validate_eof("EF0001 010004 0200010003 030000 00 00010000 60005C"), + EOFValidationError::invalid_first_section_type); + EXPECT_EQ(validate_eof("EF0001 010004 0200010002 030000 00 01000000 5000"), + EOFValidationError::invalid_first_section_type); + EXPECT_EQ(validate_eof("EF0001 010004 0200010003 030000 00 02030000 60005C"), + EOFValidationError::invalid_first_section_type); +} +TEST(eof_validation, EOF1_too_many_code_sections) +{ + const auto valid = "EF0001 011000" + bytecode{"020400"} + 0x400 * bytecode{"0001"} + + "030000 00" + 0x400 * bytecode{"00000000"} + 0x400 * bytecode{"FE"}; + EXPECT_EQ(validate_eof(valid), EOFValidationError::success); + + const auto invalid = "EF0001 011002" + bytecode{"020401"} + 0x401 * bytecode{"0001"} + + "030000 00" + 0x401 * bytecode{"00000000"} + 0x401 * bytecode{"FE"}; + EXPECT_EQ(validate_eof(invalid), EOFValidationError::too_many_code_sections); +} + +TEST(eof_validation, EOF1_undefined_opcodes) +{ const auto& gas_table = evmone::instr::gas_costs[EVMC_CANCUN]; for (uint16_t opcode = 0; opcode <= 0xff; ++opcode) { - // PUSH*, DUPN, SWAPN require immediate argument to be valid, checked in a separate test - if (opcode >= OP_PUSH1 && opcode <= OP_PUSH32) + // PUSH*, DUPN, SWAPN, RJUMP*, CALLF require immediate argument to be valid, + // checked in a separate test. + if ((opcode >= OP_PUSH1 && opcode <= OP_PUSH32) || opcode == OP_DUPN || + opcode == OP_SWAPN || opcode == OP_RJUMP || opcode == OP_RJUMPI || + opcode == OP_RJUMPV || opcode == OP_CALLF) continue; - if (opcode == OP_DUPN || opcode == OP_SWAPN) + // These opcodes are deprecated since Cancun. + // gas_cost table current implementation does not allow to undef instructions. + if (opcode == OP_JUMP || opcode == OP_JUMPI || opcode == OP_PC || opcode == OP_CALLCODE || + 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 010001 00 FE"), EOFValidationError::success); + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030000 00 00000000 FE"), + EOFValidationError::success); } TEST(eof_validation, EOF1_truncated_push) { - auto eof_header = "EF0001 010001 00"_hex; - auto& code_size_byte = eof_header[5]; + auto eof_header = "EF0001 010004 0200010001 030000 00 00000000"_hex; + auto& code_size_byte = eof_header[10]; for (uint8_t opcode = OP_PUSH1; opcode <= OP_PUSH32; ++opcode) { const auto required_bytes = static_cast(opcode) - OP_PUSH1 + 1; @@ -206,41 +376,874 @@ TEST(eof_validation, EOF1_truncated_push) code_size_byte = static_cast(code.size()); const auto container = eof_header + code; - EXPECT_EQ(validate_eof(container), EOFValidationError::missing_terminating_instruction) + EXPECT_EQ(validate_eof(container), EOFValidationError::truncated_instruction) << hex(container); } 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); } } -TEST(eof_validation, EOF1_terminating_instructions) +TEST(eof_validation, EOF1_valid_rjump) { - auto eof_header = "EF0001 010001 00"_hex; - auto& code_size_byte = eof_header[5]; + // offset = 0 + EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5C000000"), + EOFValidationError::success); - const auto& traits = evmone::instr::traits; + // offset = 3 + EXPECT_EQ(validate_eof("EF0001 010004 0200010009 030000 00 00000001 5C00036001005CFFFA"), + EOFValidationError::success); - for (uint16_t opcode = 0; opcode <= 0xff; ++opcode) + // offset = -4 + 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 00000001 60005D000000"), + EOFValidationError::success); + + // offset = 3 + EXPECT_EQ(validate_eof("EF0001 010004 0200010009 030000 00 00000001 60005D00035B5B5B00"), + EOFValidationError::success); + + // offset = -5 + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000001 60005DFFFB00"), + EOFValidationError::success); +} + +TEST(eof_validation, EOF1_valid_rjumpv) +{ + // table = [0] case = 0 + EXPECT_EQ(validate_eof("EF0001 010004 0200010009 030000 00 00000001 60005E010000600100"), + EOFValidationError::success); + + // table = [0,3] case = 0 + EXPECT_EQ( + validate_eof("EF0001 010004 020001000E 030000 00 00000001 60005E0200000003600100600200"), + EOFValidationError::success); + + // table = [0,3] case = 2 + EXPECT_EQ( + validate_eof("EF0001 010004 020001000E 030000 00 00000001 60025E0200000003600100600200"), + EOFValidationError::success); + + // table = [0,3,-10] case = 2 + EXPECT_EQ(validate_eof( + "EF0001 010004 0200010010 030000 00 00000001 60025E0300000003FFF6600100600200"), + EOFValidationError::success); +} + +TEST(eof_validation, EOF1_rjump_truncated) +{ + EXPECT_EQ(validate_eof("EF0001 010004 0200010001 030000 00 00000000 5C"), + EOFValidationError::truncated_instruction); + + EXPECT_EQ(validate_eof("EF0001 010004 0200010002 030000 00 00000000 5C00"), + EOFValidationError::truncated_instruction); +} + +TEST(eof_validation, EOF1_rjumpi_truncated) +{ + EXPECT_EQ(validate_eof("EF0001 010004 0200010003 030000 00 00000000 60005D"), + EOFValidationError::truncated_instruction); + + EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 60005D00"), + EOFValidationError::truncated_instruction); +} + +TEST(eof_validation, EOF1_rjumpv_truncated) +{ + // table = [0] case = 0 + EXPECT_EQ(validate_eof("EF0001 010004 0200010005 030000 00 00000000 60005E0100"), + EOFValidationError::truncated_instruction); + + // table = [0,3] case = 0 + EXPECT_EQ(validate_eof("EF0001 010004 0200010007 030000 00 00000000 60005E02000000"), + EOFValidationError::truncated_instruction); + + // table = [0,3] case = 2 + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60025E020000"), + EOFValidationError::truncated_instruction); + + // table = [0,3,-10] case = 2 + EXPECT_EQ(validate_eof("EF0001 010004 0200010009 030000 00 00000000 60025E0300000003FF"), + EOFValidationError::truncated_instruction); +} + +TEST(eof_validation, EOF1_rjumpv_0_count) +{ + auto code = eof1_bytecode(rjumpv({}, 0) + OP_STOP, 1); + + EXPECT_EQ(validate_eof(code), EOFValidationError::invalid_rjumpv_count); +} + +TEST(eof_validation, EOF1_rjump_invalid_destination) +{ + // Into header (offset = -5) + EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5CFFFB00"), + EOFValidationError::invalid_rjump_destination); + + // To before code begin (offset = -13) + EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5CFFF300"), + EOFValidationError::invalid_rjump_destination); + + // To after code end (offset = 2) + EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5C000200"), + EOFValidationError::invalid_rjump_destination); + + // To code end (offset = 1) + EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5C000100"), + EOFValidationError::invalid_rjump_destination); + + // To the same RJUMP immediate (offset = -1) + EXPECT_EQ(validate_eof("EF0001 010004 0200010004 030000 00 00000000 5CFFFF00"), + EOFValidationError::invalid_rjump_destination); + + // To PUSH immediate (offset = -4) + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005CFFFC00"), + EOFValidationError::invalid_rjump_destination); +} + +TEST(eof_validation, EOF1_rjumpi_invalid_destination) +{ + // Into header (offset = -7) + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005DFFF900"), + EOFValidationError::invalid_rjump_destination); + + // To before code begin (offset = -15) + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005DFFF100"), + EOFValidationError::invalid_rjump_destination); + + // To after code end (offset = 2) + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005D000200"), + EOFValidationError::invalid_rjump_destination); + + // To code end (offset = 1) + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005D000100"), + EOFValidationError::invalid_rjump_destination); + + // To the same RJUMPI immediate (offset = -1) + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005DFFFF00"), + EOFValidationError::invalid_rjump_destination); + + // To PUSH immediate (offset = -4) + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030000 00 00000000 60005DFFFC00"), + EOFValidationError::invalid_rjump_destination); +} + +TEST(eof_validation, EOF1_rjumpv_invalid_destination) +{ + // table = [-23] case = 0 + EXPECT_EQ(validate_eof("EF0001 010004 0200010008 030000 00 00000000 60005E01FFE96001"), + EOFValidationError::invalid_rjump_destination); + + // table = [-8] case = 0 + EXPECT_EQ(validate_eof("EF0001 010004 0200010008 030000 00 00000000 60005E01FFF86001"), + EOFValidationError::invalid_rjump_destination); + + // table = [-1] case = 0 + EXPECT_EQ(validate_eof("EF0001 010004 0200010008 030000 00 00000000 60005E01FFFF6001"), + EOFValidationError::invalid_rjump_destination); + + // table = [2] case = 0 + EXPECT_EQ(validate_eof("EF0001 010004 0200010008 030000 00 00000000 60005E0100026001"), + EOFValidationError::invalid_rjump_destination); + + // table = [3] case = 0 + EXPECT_EQ(validate_eof("EF0001 010004 0200010008 030000 00 00000000 60005E0100036001"), + EOFValidationError::invalid_rjump_destination); + + + // table = [0,3,-27] case = 2 + EXPECT_EQ( + validate_eof("EF0001 010004 020001000F 030000 00 00000000 60025E0300000003FFE56001006002"), + EOFValidationError::invalid_rjump_destination); + + // table = [0,3,-12] case = 2 + EXPECT_EQ( + validate_eof("EF0001 010004 020001000F 030000 00 00000000 60025E0300000003FFF46001006002"), + EOFValidationError::invalid_rjump_destination); + + // table = [0,3,-1] case = 2 + EXPECT_EQ( + validate_eof("EF0001 010004 020001000F 030000 00 00000000 60025E0300000003FFFF6001006002"), + EOFValidationError::invalid_rjump_destination); + + // table = [0,3,5] case = 2 + EXPECT_EQ( + validate_eof("EF0001 010004 020001000F 030000 00 00000000 60025E030000000300056001006002"), + EOFValidationError::invalid_rjump_destination); + + // table = [0,3,6] case = 2 + EXPECT_EQ( + validate_eof("EF0001 010004 020001000F 030000 00 00000000 60025E030000000300066001006002"), + EOFValidationError::invalid_rjump_destination); +} + +TEST(eof_validation, EOF1_section_order) +{ + // 01 02 03 + EXPECT_EQ(validate_eof("EF0001 010004 0200010006 030002 00 00000001 60005D000000 AABB"), + EOFValidationError::success); + + // 01 03 02 + EXPECT_EQ(validate_eof("EF0001 010004 030002 0200010006 00 00000000 AABB 60005D000000"), + EOFValidationError::code_section_missing); + + // 02 01 03 + EXPECT_EQ(validate_eof("EF0001 0200010006 010004 030002 00 60005D000000 00000000 AABB"), + EOFValidationError::type_section_missing); + + // 02 03 01 + EXPECT_EQ(validate_eof("EF0001 0200010006 030002 010004 00 60005D000000 AABB 00000000"), + EOFValidationError::type_section_missing); + + // 03 01 02 + EXPECT_EQ(validate_eof("EF0001 030002 010004 0200010006 00 AABB 00000000 60005D000000"), + EOFValidationError::type_section_missing); + + // 03 02 01 + EXPECT_EQ(validate_eof("EF0001 030002 0200010006 010004 00 AABB 60005D000000 00000000"), + EOFValidationError::type_section_missing); +} + +TEST(eof_validation, deprecated_instructions) +{ + for (auto op : {OP_CALLCODE, OP_SELFDESTRUCT, OP_JUMP, OP_JUMPI, OP_PC}) { - const auto& op_traits = traits[opcode]; - // Skip undefined opcodes. - // TODO: iterate over all EOF revisions. - if (op_traits.name == nullptr || op_traits.since == EVMC_CANCUN) - continue; + EXPECT_EQ(validate_eof(eof1_bytecode(op)), EOFValidationError::undefined_instruction); + } +} - const auto code = static_cast(opcode) + bytes(op_traits.immediate_size, 0); - code_size_byte = static_cast(code.size()); - const auto container = eof_header + code; +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); + } - const auto expected = ((opcode == OP_STOP || opcode == OP_RETURN || opcode == OP_REVERT || - opcode == OP_INVALID || opcode == OP_SELFDESTRUCT) ? - EOFValidationError::success : - EOFValidationError::missing_terminating_instruction); - EXPECT_EQ(validate_eof(container), expected) << hex(code); + { + 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( + validate_eof("ef0001 010100 02003f 0100"), EOFValidationError::incomplete_section_size); +} + +TEST(eof_validation, data_section_missing) +{ + EXPECT_EQ(validate_eof("ef0001 010004 0200010001 00 00000000 fe"), + EOFValidationError::data_section_missing); +} + +TEST(eof_validation, multiple_code_sections_headers) +{ + EXPECT_EQ(validate_eof("0xef0001 010008 020001 0004 020001 0005 030000 00 00040000 045c0000 " + "00405c00 00002e0005"), + EOFValidationError::data_section_missing); +} + +TEST(eof_validation, many_code_sections_1023) +{ + auto code = + "0xef0001010ffc0203ff0001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010300000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000"; + EXPECT_EQ(validate_eof(code), EOFValidationError::success); +} + +TEST(eof_validation, many_code_sections_1024) +{ + auto code = + "0xef00010110000204000001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001030000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000"; + EXPECT_EQ(validate_eof(code), EOFValidationError::success); +} + +TEST(eof_validation, too_many_code_sections) +{ + auto code = + "0xef00010110040204010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001" + "000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100" + "010001000100010001000100010001000100010001000100010001000100010001000103000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000"; + EXPECT_EQ(validate_eof(code), EOFValidationError::too_many_code_sections); +} diff --git a/test/unittests/evm_eof_function_test.cpp b/test/unittests/evm_eof_function_test.cpp new file mode 100644 index 0000000000..df185c6672 --- /dev/null +++ b/test/unittests/evm_eof_function_test.cpp @@ -0,0 +1,59 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2022 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "evm_fixture.hpp" +#include "evmone/eof.hpp" + +using evmone::test::evm; + +TEST_P(evm, eof_function_example1) +{ + // Relative jumps are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_CANCUN; + const auto code = + "EF00 01 010008 020002 000f 0002 030000 00" + "00000002 02010002" + "6001 6008 b00001 " + + ret_top() + "03b1"; + + ASSERT_EQ((int)evmone::validate_eof(rev, code), (int)evmone::EOFValidationError{}); + + execute(code); + EXPECT_GAS_USED(EVMC_SUCCESS, 32); + EXPECT_OUTPUT_INT(7); +} + +TEST_P(evm, eof_function_example2) +{ + // Relative jumps are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_CANCUN; + const auto code = + "ef0001 01000c 020003 003b 0017 001d 030000 00 00000004 01010003 01010004" + "60043560003560e01c63c766526781145d001c63c6c2ea1781145d00065050600080fd50b00002600052602060" + "00f350b0000160005260206000f3" + "600181115d0004506001b160018103b0000181029050b1" + "600281115d0004506001b160028103b0000260018203b00002019050b1"_hex; + + ASSERT_EQ((int)evmone::validate_eof(rev, code), (int)evmone::EOFValidationError{}); + + // Call fac(5) + const auto calldata_fac = + "c76652670000000000000000000000000000000000000000000000000000000000000005"_hex; + execute(bytecode{code}, calldata_fac); + EXPECT_GAS_USED(EVMC_SUCCESS, 246); + EXPECT_EQ(output, "0000000000000000000000000000000000000000000000000000000000000078"_hex); + + // Call fib(15) + const auto calldata_fib = + "c6c2ea17000000000000000000000000000000000000000000000000000000000000000f"_hex; + execute(bytecode{code}, calldata_fib); + EXPECT_GAS_USED(EVMC_SUCCESS, 44544); + EXPECT_EQ(output, "0000000000000000000000000000000000000000000000000000000000000262"_hex); +} diff --git a/test/unittests/evm_eof_rjump_test.cpp b/test/unittests/evm_eof_rjump_test.cpp new file mode 100644 index 0000000000..9c71ee990c --- /dev/null +++ b/test/unittests/evm_eof_rjump_test.cpp @@ -0,0 +1,236 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2022 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include "evm_fixture.hpp" +#include "evmone/eof.hpp" + +using evmone::test::evm; + + +TEST_P(evm, eof1_rjump) +{ + // Relative jumps are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_CANCUN; + 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( + rjumpi(3, 0) + rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1), 2, "deadbeef"); + + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 1); + EXPECT_EQ(result.output_data[0], 1); +} + +TEST_P(evm, eof1_rjump_backward) +{ + // Relative jumps are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_CANCUN; + 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(10) + mstore8(0, 1) + ret(0, 1) + rjump(-13), 2, "deadbeef"); + + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 1); + EXPECT_EQ(result.output_data[0], 1); +} + +TEST_P(evm, eof1_rjump_0_offset) +{ + // Relative jumps are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_CANCUN; + auto code = eof1_bytecode(rjump(0) + 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); +} + +TEST_P(evm, eof1_rjumpi) +{ + // Relative jumps are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_CANCUN; + auto code = eof1_bytecode( + 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); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 1); + EXPECT_EQ(result.output_data[0], 1); + + // RJUMPI condition is false + execute(code, "00"_hex); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 1); + EXPECT_EQ(result.output_data[0], 2); +} + +TEST_P(evm, eof1_rjumpi_backwards) +{ + // Relative jumps are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_CANCUN; + 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); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 1); + EXPECT_EQ(result.output_data[0], 1); + + // RJUMPI condition is false + execute(code, "00"_hex); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 1); + EXPECT_EQ(result.output_data[0], 2); +} + +TEST_P(evm, eof1_rjumpi_0_offset) +{ + // Relative jumps are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_CANCUN; + auto code = eof1_bytecode(rjumpi(0, calldataload(0)) + mstore8(0, 1) + ret(0, 1), 2); + + // RJUMPI condition is true + execute(code, "01"_hex); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 1); + EXPECT_EQ(result.output_data[0], 1); + + // RJUMPI condition is false + execute(code, "00"_hex); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 1); + EXPECT_EQ(result.output_data[0], 1); +} + +TEST_P(evm, eof1_rjumpv_single_offset) +{ + // Relative jumps are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_CANCUN; + auto code = eof1_bytecode(rjumpv({3}, 0) + OP_JUMPDEST + OP_JUMPDEST + OP_STOP + 20 + 40 + 0 + + OP_CODECOPY + ret(0, 20), + 3, "ef000101000402000100010300000000000000fe"); + + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 20); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "ef000101000402000100010300000000000000fe"_hex); +} + +TEST_P(evm, eof1_rjumpv_multiple_offsets) +{ + // Relative jumps are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_CANCUN; + 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), + 3, "ef000101000402000100010300000000000000fe"); + + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 10); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "ef000101000402000100"_hex); + + auto& rjumpv_cond = code[35]; + + rjumpv_cond = 2; + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 10); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "010300000000000000fe"_hex); + + rjumpv_cond = 0; + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 20); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), + "ef000101000402000100010300000000000000fe"_hex); + + rjumpv_cond = 12; // case >= count, same behaviour as for case == 2 + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + ASSERT_EQ(result.output_size, 10); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "010300000000000000fe"_hex); +} + +TEST_P(evm, eof1_rjumpv_long_jumps) +{ + // Relative jumps are not implemented in Advanced. + if (is_advanced()) + return; + + rev = EVMC_CANCUN; + auto code = + rjump(0x7fff - 3 - 5) + (0x7fff - 3 - 2 - 8 - 5) * bytecode{OP_JUMPDEST} + 7 + ret_top(); + + code += rjumpv({-0x7FFF, 0x7FFF - 8 - 2 - 8}, 0) + + (0x7fff - 8 - 2 - 8) * bytecode{OP_JUMPDEST} + 5 + ret_top(); + + code = eof1_bytecode(code, 2); + auto& rjumpv_cond = code[0x7fff - 3 - 5 + 3 + 1 + 19]; + + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(7); + + rjumpv_cond = 1; + + execute(code); + EXPECT_STATUS(EVMC_SUCCESS); + EXPECT_OUTPUT_INT(5); +} + +TEST_P(evm, rjumps_undefined_in_legacy) +{ + rev = EVMC_CANCUN; + auto code = rjump(1) + OP_INVALID + mstore8(0, 1) + ret(0, 1); + + execute(code); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); + + code = rjumpi(10, 1) + mstore8(0, 2) + ret(0, 1) + mstore8(0, 1) + ret(0, 1); + + execute(code); + EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION); +} diff --git a/test/unittests/evm_eof_test.cpp b/test/unittests/evm_eof_test.cpp index c4ebe59570..dc31cbc2d3 100644 --- a/test/unittests/evm_eof_test.cpp +++ b/test/unittests/evm_eof_test.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "evm_fixture.hpp" +#include "evmone/eof.hpp" using evmone::test::evm; @@ -23,176 +24,88 @@ 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); EXPECT_EQ(result.output_size, 0); } -TEST_P(evm, eof1_pc) -{ - rev = EVMC_CANCUN; - auto code = eof1_bytecode(OP_PC + mstore8(0) + ret(0, 1)); - - execute(code); - EXPECT_STATUS(EVMC_SUCCESS); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 0); - - code = eof1_bytecode(4 * bytecode{OP_JUMPDEST} + OP_PC + mstore8(0) + ret(0, 1)); - - execute(code); - EXPECT_STATUS(EVMC_SUCCESS); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 4); -} - -TEST_P(evm, eof1_jump_inside_code_section) -{ - rev = EVMC_CANCUN; - auto code = eof1_bytecode(jump(4) + OP_INVALID + OP_JUMPDEST + mstore8(0, 1) + ret(0, 1)); - - execute(code); - EXPECT_STATUS(EVMC_SUCCESS); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 1); - - code = - eof1_bytecode(jump(4) + OP_INVALID + OP_JUMPDEST + mstore8(0, 1) + ret(0, 1), "deadbeef"); - - execute(code); - EXPECT_STATUS(EVMC_SUCCESS); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 1); -} - -TEST_P(evm, eof1_jumpi_inside_code_section) -{ - rev = EVMC_CANCUN; - auto code = eof1_bytecode(jumpi(6, 1) + OP_INVALID + OP_JUMPDEST + mstore8(0, 1) + ret(0, 1)); - - execute(code); - EXPECT_STATUS(EVMC_SUCCESS); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 1); - - code = eof1_bytecode( - jumpi(6, 1) + OP_INVALID + OP_JUMPDEST + mstore8(0, 1) + ret(0, 1), "deadbeef"); - - execute(code); - EXPECT_STATUS(EVMC_SUCCESS); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 1); -} - -TEST_P(evm, eof1_jump_into_data_section) -{ - rev = EVMC_CANCUN; - // data section contains OP_JUMPDEST + mstore8(0, 1) + ret(0, 1) - const auto code = eof1_bytecode(jump(4) + OP_STOP, OP_JUMPDEST + mstore8(0, 1) + ret(0, 1)); - - execute(code); - EXPECT_STATUS(EVMC_BAD_JUMP_DESTINATION); -} - -TEST_P(evm, eof1_jumpi_into_data_section) -{ - rev = EVMC_CANCUN; - // data section contains OP_JUMPDEST + mstore8(0, 1) + ret(0, 1) - const auto code = eof1_bytecode(jumpi(6, 1) + OP_STOP, OP_JUMPDEST + mstore8(0, 1) + ret(0, 1)); - - execute(code); - EXPECT_STATUS(EVMC_BAD_JUMP_DESTINATION); -} - -TEST_P(evm, eof1_push_byte_in_header) -{ - rev = EVMC_CANCUN; - // data section is 0x65 bytes long, so header contains 0x65 (PUSH6) byte, - // but it must not affect jumpdest analysis (OP_JUMPDEST stays valid) - auto code = eof1_bytecode( - jump(4) + OP_INVALID + OP_JUMPDEST + mstore8(0, 1) + ret(0, 1), bytes(0x65, '\0')); - - execute(code); - EXPECT_STATUS(EVMC_SUCCESS); - ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 1); -} - 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], 16); + 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); ASSERT_EQ(result.output_size, 1); - EXPECT_EQ(result.output_data[0], 23); + EXPECT_EQ(result.output_data[0], 32); } TEST_P(evm, eof1_codecopy_full) { rev = EVMC_CANCUN; - auto code = eof1_bytecode(bytecode{19} + 0 + 0 + OP_CODECOPY + ret(0, 19)); + 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), - "ef000101000c006013600060003960136000f3"_hex); + "ef0001010004020001000c0300000000000003601f6000600039601f6000f3"_hex); - code = eof1_bytecode(bytecode{26} + 0 + 0 + OP_CODECOPY + ret(0, 26), "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), - "ef000101000c02000400601a6000600039601a6000f3deadbeef"_hex); + "ef0001010004020001000c03000400000000036023600060003960236000f3deadbeef"_hex); } TEST_P(evm, eof1_codecopy_header) { rev = EVMC_CANCUN; - auto code = eof1_bytecode(bytecode{7} + 0 + 0 + OP_CODECOPY + ret(0, 7)); + 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), "ef000101000c00"_hex); + EXPECT_EQ( + bytes_view(result.output_data, result.output_size), "ef0001010004020001000c03000000"_hex); - code = eof1_bytecode(bytecode{10} + 0 + 0 + OP_CODECOPY + ret(0, 10), "deadbeef"); + code = eof1_bytecode(bytecode{15} + 0 + 0 + OP_CODECOPY + ret(0, 15), 3, "deadbeef"); execute(code); EXPECT_STATUS(EVMC_SUCCESS); - EXPECT_EQ(bytes_view(result.output_data, result.output_size), "ef000101000c02000400"_hex); + EXPECT_EQ( + bytes_view(result.output_data, result.output_size), "ef0001010004020001000c03000400"_hex); } TEST_P(evm, eof1_codecopy_code) { rev = EVMC_CANCUN; - auto code = eof1_bytecode(bytecode{12} + 7 + 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), "600c6007600039600c6000f3"_hex); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "600c6013600039600c6000f3"_hex); - code = eof1_bytecode(bytecode{12} + 10 + 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); - EXPECT_EQ(bytes_view(result.output_data, result.output_size), "600c600a600039600c6000f3"_hex); + EXPECT_EQ(bytes_view(result.output_data, result.output_size), "600c6013600039600c6000f3"_hex); } TEST_P(evm, eof1_codecopy_data) { rev = EVMC_CANCUN; - const auto code = eof1_bytecode(bytecode{4} + 22 + 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); @@ -203,17 +116,36 @@ 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{23} + 0 + 0 + OP_CODECOPY + ret(0, 23)); + 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), - "ef000101000c006017600060003960176000f300000000"_hex); + "ef0001010004020001000c03000000000000036023600060003960236000f300000000"_hex); - code = eof1_bytecode(bytecode{30} + 0 + 0 + OP_CODECOPY + ret(0, 30), "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), - "ef000101000c02000400601e6000600039601e6000f3deadbeef00000000"_hex); + "ef0001010004020001000c03000400000000036027600060003960276000f3deadbeef00000000"_hex); +} + +TEST_P(evm, eof_data_only_contract) +{ + rev = EVMC_CANCUN; + auto code = "EF0001 010004 020001 0001 03daaa 00 00000000 FE"_hex; + const auto data_size_ptr = &code[code.find(0xda)]; + + intx::be::unsafe::store(data_size_ptr, uint16_t{0}); + execute(code); + EXPECT_STATUS(EVMC_INVALID_INSTRUCTION); + + intx::be::unsafe::store(data_size_ptr, uint16_t{1}); + execute(code + "aa"_hex); + EXPECT_STATUS(EVMC_INVALID_INSTRUCTION); + + intx::be::unsafe::store(data_size_ptr, uint16_t{256}); + execute(code + bytes(256, 0x01)); + EXPECT_STATUS(EVMC_INVALID_INSTRUCTION); } diff --git a/test/unittests/instructions_test.cpp b/test/unittests/instructions_test.cpp index 1a943b97d2..2e13a3799d 100644 --- a/test/unittests/instructions_test.cpp +++ b/test/unittests/instructions_test.cpp @@ -38,6 +38,7 @@ constexpr bool is_terminating(Opcode op) noexcept { case OP_STOP: case OP_RETURN: + case OP_RETF: case OP_REVERT: case OP_INVALID: case OP_SELFDESTRUCT: @@ -55,15 +56,15 @@ constexpr void validate_traits_of() noexcept // immediate_size if constexpr (Op >= OP_PUSH1 && Op <= OP_PUSH32) static_assert(tr.immediate_size == Op - OP_PUSH1 + 1); + else if constexpr (Op == OP_RJUMP || Op == OP_RJUMPI || Op == OP_CALLF) + static_assert(tr.immediate_size == 2); else if constexpr (Op == OP_DUPN || Op == OP_SWAPN) static_assert(tr.immediate_size == 1); else - static_assert(tr.immediate_size == 0); + static_assert(tr.immediate_size == 0); // Including RJUMPV. // is_terminating static_assert(tr.is_terminating == is_terminating(Op)); - static_assert(!tr.is_terminating || tr.immediate_size == 0, - "terminating instructions must not have immediate bytes - this simplifies EOF validation"); // since constexpr auto expected_rev = get_revision_defined_in(Op); @@ -89,6 +90,7 @@ static_assert(!instr::has_const_gas_cost(OP_SHL)); static_assert(!instr::has_const_gas_cost(OP_BALANCE)); static_assert(!instr::has_const_gas_cost(OP_SLOAD)); } // namespace + } // namespace evmone::test namespace @@ -101,6 +103,11 @@ constexpr bool instruction_only_in_evmone(evmc_revision rev, Opcode op) noexcept switch (op) { + case OP_RJUMP: + case OP_RJUMPI: + case OP_RJUMPV: + case OP_CALLF: + case OP_RETF: case OP_DUPN: case OP_SWAPN: return true; @@ -123,6 +130,7 @@ TEST(instructions, compare_with_evmc_instruction_tables) { if (instruction_only_in_evmone(rev, Opcode(i))) continue; + const auto gas_cost = (instr_tbl[i] != instr::undefined) ? instr_tbl[i] : 0; const auto& metrics = evmone_tbl[i]; const auto& ref_metrics = evmc_tbl[i]; @@ -154,6 +162,7 @@ TEST(instructions, compare_undefined_instructions) { if (instruction_only_in_evmone(rev, Opcode(i))) continue; + EXPECT_EQ(instr_tbl[i] == instr::undefined, evmc_names_tbl[i] == nullptr) << i; } } @@ -166,6 +175,7 @@ TEST(instructions, compare_with_evmc_instruction_names) { if (instruction_only_in_evmone(EVMC_MAX_REVISION, Opcode(i))) continue; + EXPECT_STREQ(instr::traits[i].name, evmc_tbl[i]); } } 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 5cd9b3e53e..d5f556d435 100644 --- a/test/utils/bytecode.hpp +++ b/test/utils/bytecode.hpp @@ -83,34 +83,36 @@ inline bytecode operator*(int n, Opcode op) return n * bytecode{op}; } -inline bytes big_endian(uint16_t value) +template +inline typename std::enable_if_t || std::is_same_v, bytes> +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(code_size); - - if (data_size != 0) - out += "02" + big_endian(data_size); - + 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 += "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; } @@ -242,6 +244,24 @@ inline bytecode jumpi(bytecode target, bytecode condition) return condition + target + OP_JUMPI; } +inline bytecode rjump(int16_t offset) +{ + return OP_RJUMP + big_endian(offset); +} + +inline bytecode rjumpi(int16_t offset, bytecode condition) +{ + return condition + (OP_RJUMPI + big_endian(offset)); +} + +inline bytecode rjumpv(const std::initializer_list offsets, bytecode condition) +{ + bytecode ret = condition + OP_RJUMPV + static_cast(offsets.size()); + for (const auto offset : offsets) + ret += bytecode{big_endian(offset)}; + return ret; +} + inline bytecode ret(bytecode index, bytecode size) { return size + index + OP_RETURN;