diff --git a/lib/evmone/eof.cpp b/lib/evmone/eof.cpp index e55aaef073..83a8284f25 100644 --- a/lib/evmone/eof.cpp +++ b/lib/evmone/eof.cpp @@ -191,4 +191,44 @@ EOFValidationError validate_eof(evmc_revision rev, bytes_view container) noexcep else return EOFValidationError::eof_version_unknown; } + +std::string_view get_error_message(EOFValidationError err) noexcept +{ + switch (err) + { + case EOFValidationError::success: + return "success"; + case EOFValidationError::starts_with_format: + return "starts_with_format"; + case EOFValidationError::invalid_prefix: + return "invalid_prefix"; + case EOFValidationError::eof_version_mismatch: + return "eof_version_mismatch"; + case EOFValidationError::eof_version_unknown: + return "eof_version_unknown"; + case EOFValidationError::incomplete_section_size: + return "incomplete_section_size"; + 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::zero_section_size: + return "zero_section_size"; + case EOFValidationError::section_headers_not_terminated: + return "section_headers_not_terminated"; + case EOFValidationError::invalid_section_bodies_size: + return "invalid_section_bodies_size"; + case EOFValidationError::undefined_instruction: + return "undefined_instruction"; + case EOFValidationError::missing_terminating_instruction: + return "missing_terminating_instruction"; + case EOFValidationError::impossible: + return "impossible"; + } + return ""; +} } // namespace evmone diff --git a/lib/evmone/eof.hpp b/lib/evmone/eof.hpp index ed9faef28c..4b9f64c694 100644 --- a/lib/evmone/eof.hpp +++ b/lib/evmone/eof.hpp @@ -59,4 +59,7 @@ enum class EOFValidationError /// Validates whether given container is a valid EOF according to the rules of given revision. [[nodiscard]] EVMC_EXPORT EOFValidationError validate_eof( evmc_revision rev, bytes_view container) noexcept; + +/// Returns the error message corresponding to an error code. +[[nodiscard]] EVMC_EXPORT std::string_view get_error_message(EOFValidationError err) noexcept; } // namespace evmone diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c1061d8317..1c5bbe077d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,6 +15,7 @@ find_package(benchmark CONFIG REQUIRED) add_subdirectory(utils) add_subdirectory(bench) +add_subdirectory(eofparse) add_subdirectory(integration) add_subdirectory(internal_benchmarks) add_subdirectory(state) @@ -22,11 +23,12 @@ add_subdirectory(statetest) add_subdirectory(t8n) add_subdirectory(unittests) -set(targets evmone-bench evmone-bench-internal evmone-state evmone-statetest evmone-t8n evmone-unittests testutils) +set(targets evmone-bench evmone-bench-internal evmone-eofparse evmone-state evmone-statetest evmone-t8n evmone-unittests testutils) if(EVMONE_FUZZING) + add_subdirectory(eofparsefuzz) add_subdirectory(fuzzer) - list(APPEND targets evmone-fuzzer) + list(APPEND targets evmone-eofparsefuzz evmone-fuzzer) endif() set_target_properties( diff --git a/test/eofparse/CMakeLists.txt b/test/eofparse/CMakeLists.txt new file mode 100644 index 0000000000..e05cfee577 --- /dev/null +++ b/test/eofparse/CMakeLists.txt @@ -0,0 +1,7 @@ +# evmone: Fast Ethereum Virtual Machine implementation +# Copyright 2023 The evmone Authors. +# SPDX-License-Identifier: Apache-2.0 + +add_executable(evmone-eofparse eofparse.cpp) +target_link_libraries(evmone-eofparse PRIVATE evmone) +target_include_directories(evmone-eofparse PRIVATE ${evmone_private_include_dir}) diff --git a/test/eofparse/eofparse.cpp b/test/eofparse/eofparse.cpp new file mode 100644 index 0000000000..cc9ebe4361 --- /dev/null +++ b/test/eofparse/eofparse.cpp @@ -0,0 +1,72 @@ +// evmone: Fast Ethereum Virtual Machine implementation +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include + +namespace +{ +inline constexpr bool isalnum(char ch) noexcept +{ + return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); +} + +template +struct skip_nonalnum_iterator : evmc::filter_iterator +{ + using evmc::filter_iterator::filter_iterator; +}; + +template +skip_nonalnum_iterator(BaseIterator, BaseIterator) -> skip_nonalnum_iterator; + +template +std::optional from_hex_skip_nonalnum(InputIterator begin, InputIterator end) noexcept +{ + evmc::bytes bs; + if (!from_hex(skip_nonalnum_iterator{begin, end}, skip_nonalnum_iterator{end, end}, + std::back_inserter(bs))) + return {}; + return bs; +} + +} // namespace + +int main() +{ + try + { + for (std::string line; std::getline(std::cin, line);) + { + if (line.empty() || line.starts_with('#')) + continue; + + auto o = from_hex_skip_nonalnum(line.begin(), line.end()); + if (!o) + { + std::cout << "err: invalid hex\n"; + continue; + } + + const auto& eof = *o; + const auto err = evmone::validate_eof(EVMC_CANCUN, eof); + if (err != evmone::EOFValidationError::success) + { + std::cout << "err: " << evmone::get_error_message(err) << "\n"; + continue; + } + + const auto header = evmone::read_valid_eof1_header(evmone::bytes_view{eof}.begin()); + std::cout << "OK " << evmc::hex({&eof[header.code_begin()], header.code_size}) << "\n"; + } + return 0; + } + catch (const std::exception& ex) + { + std::cerr << ex.what() << "\n"; + return 1; + } +} diff --git a/test/eofparsefuzz/CMakeLists.txt b/test/eofparsefuzz/CMakeLists.txt new file mode 100644 index 0000000000..e2e72abeef --- /dev/null +++ b/test/eofparsefuzz/CMakeLists.txt @@ -0,0 +1,18 @@ +# evmone-fuzzer: LibFuzzer based testing tool for EVMC-compatible EVM implementations. +# Copyright 2023 The evmone Authors. +# SPDX-License-Identifier: Apache-2.0 + +get_target_property(type evmone TYPE) +if(NOT type STREQUAL STATIC_LIBRARY) + message(FATAL_ERROR "The evmone must be built as static library") +endif() + +if(fuzzing_coverage) + set(CMAKE_EXE_LINKER_FLAGS "-fsanitize=fuzzer") +else() + string(REPLACE fuzzer-no-link fuzzer CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}) +endif() + +add_executable(evmone-eofparsefuzz eofparsefuzz.cpp) +target_link_libraries(evmone-eofparsefuzz PRIVATE evmone) +target_include_directories(evmone-eofparsefuzz PRIVATE ${evmone_private_include_dir}) diff --git a/test/eofparsefuzz/eofparsefuzz.cpp b/test/eofparsefuzz/eofparsefuzz.cpp new file mode 100644 index 0000000000..ff05832bc8 --- /dev/null +++ b/test/eofparsefuzz/eofparsefuzz.cpp @@ -0,0 +1,13 @@ +// evmone-fuzzer: LibFuzzer based testing tool for EVMC-compatible EVM implementations. +// Copyright 2023 The evmone Authors. +// SPDX-License-Identifier: Apache-2.0 + +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t data_size) noexcept +{ + const evmone::bytes_view eof{data, data_size}; + if (evmone::validate_eof(EVMC_CANCUN, eof) == evmone::EOFValidationError::success) + (void)evmone::read_valid_eof1_header(eof.begin()); + return 0; +} diff --git a/test/unittests/eof_validation_test.cpp b/test/unittests/eof_validation_test.cpp index f71c9d7f8e..79d42c3531 100644 --- a/test/unittests/eof_validation_test.cpp +++ b/test/unittests/eof_validation_test.cpp @@ -20,6 +20,14 @@ inline EOFValidationError validate_eof( } } // namespace +TEST(eof_validation, get_error_message) +{ + EXPECT_EQ(evmone::get_error_message(EOFValidationError::success), "success"); + EXPECT_EQ(evmone::get_error_message(EOFValidationError::invalid_prefix), "invalid_prefix"); + EXPECT_EQ(evmone::get_error_message(EOFValidationError::impossible), "impossible"); + EXPECT_EQ(evmone::get_error_message(static_cast(-1)), ""); +} + TEST(eof_validation, validate_empty_code) { EXPECT_EQ(validate_eof(""), EOFValidationError::invalid_prefix);