diff --git a/test/unittests/eof_validation_test.cpp b/test/unittests/eof_validation_test.cpp index cbd89bfb2d..a8f0c72485 100644 --- a/test/unittests/eof_validation_test.cpp +++ b/test/unittests/eof_validation_test.cpp @@ -3,17 +3,20 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include +#include #include using namespace evmone; namespace { +// Can be called as validate_eof(string_view hex, rev) or validate_eof(bytes_view cont, rev). inline EOFValidationError validate_eof( - std::string_view code_hex, evmc_revision rev = EVMC_SHANGHAI) noexcept + const bytecode& container, evmc_revision rev = EVMC_SHANGHAI) noexcept { - return ::validate_eof(rev, from_hex(code_hex)); + return evmone::validate_eof(rev, container); } } // namespace @@ -156,3 +159,77 @@ TEST(eof_validation, EOF1_trailing_bytes) EXPECT_EQ(validate_eof("EF0001 010001 020002 00 FE AABB DEADBEEF"), EOFValidationError::invalid_section_bodies_size); } + +TEST(eof_validation, EOF1_undefined_opcodes) +{ + auto cont = from_hex("EF0001 010002 00 0000"); + + const auto& gas_table = evmone::instr::gas_costs[EVMC_SHANGHAI]; + + for (uint16_t opcode = 0; opcode <= 0xff; ++opcode) + { + // PUSH* require immediate argument to be valid, checked in a separate test + if (opcode >= OP_PUSH1 && opcode <= OP_PUSH32) + continue; + + cont[cont.size() - 2] = static_cast(opcode); + + const auto expected = (gas_table[opcode] == evmone::instr::undefined ? + EOFValidationError::undefined_instruction : + EOFValidationError::success); + EXPECT_EQ(validate_eof(cont), expected) << hex(cont); + } + + EXPECT_EQ(validate_eof(from_hex("EF0001 010001 00 FE")), EOFValidationError::success); +} + +TEST(eof_validation, EOF1_truncated_push) +{ + auto eof_header = from_hex("EF0001 010001 00"); + auto& code_size_byte = eof_header[5]; + for (uint8_t opcode = OP_PUSH1; opcode <= OP_PUSH32; ++opcode) + { + const auto required_bytes = static_cast(opcode) - OP_PUSH1 + 1; + for (size_t i = 0; i < required_bytes; ++i) + { + const bytes code{opcode + bytes(i, 0)}; + code_size_byte = static_cast(code.size()); + const auto container = eof_header + code; + + EXPECT_EQ(validate_eof(container), EOFValidationError::missing_terminating_instruction) + << hex(container); + } + + const bytes code{opcode + bytes(required_bytes, 0) + uint8_t{OP_STOP}}; + code_size_byte = static_cast(code.size()); + const auto container = eof_header + code; + + EXPECT_EQ(validate_eof(container), EOFValidationError::success) << hex(container); + } +} + +TEST(eof_validation, EOF1_terminating_instructions) +{ + auto eof_header = from_hex("EF0001 010001 00"); + auto& code_size_byte = eof_header[5]; + + const auto& traits = evmone::instr::traits; + + for (uint16_t opcode = 0; opcode <= 0xff; ++opcode) + { + const auto& op_traits = traits[opcode]; + // Skip undefined opcodes. + if (op_traits.name == nullptr) + continue; + + bytes code{static_cast(opcode) + bytes(op_traits.immediate_size, 0)}; + code_size_byte = static_cast(code.size()); + const auto container = eof_header + code; + + 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); + } +}