Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce eofparse and eofparsefuzz #568

Merged
merged 4 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions lib/evmone/eof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<unknown>";
}
} // namespace evmone
3 changes: 3 additions & 0 deletions lib/evmone/eof.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 4 additions & 2 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ find_package(benchmark CONFIG REQUIRED)

add_subdirectory(utils)
add_subdirectory(bench)
add_subdirectory(eofparse)
add_subdirectory(integration)
add_subdirectory(internal_benchmarks)
add_subdirectory(state)
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(
Expand Down
7 changes: 7 additions & 0 deletions test/eofparse/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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})
72 changes: 72 additions & 0 deletions test/eofparse/eofparse.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2023 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include <evmc/evmc.hpp>
#include <evmone/eof.hpp>
#include <iostream>
#include <string>

namespace
{
inline constexpr bool isalnum(char ch) noexcept
{
return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
}

template <typename BaseIterator>
struct skip_nonalnum_iterator : evmc::filter_iterator<BaseIterator, isalnum>
{
using evmc::filter_iterator<BaseIterator, isalnum>::filter_iterator;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

surprised that such thing is in public evmc API

};

template <typename BaseIterator>
skip_nonalnum_iterator(BaseIterator, BaseIterator) -> skip_nonalnum_iterator<BaseIterator>;

template <typename InputIterator>
std::optional<evmc::bytes> 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());
gumb0 marked this conversation as resolved.
Show resolved Hide resolved
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;
}
}
18 changes: 18 additions & 0 deletions test/eofparsefuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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})
13 changes: 13 additions & 0 deletions test/eofparsefuzz/eofparsefuzz.cpp
Original file line number Diff line number Diff line change
@@ -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 <evmone/eof.hpp>

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;
}
8 changes: 8 additions & 0 deletions test/unittests/eof_validation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<EOFValidationError>(-1)), "<unknown>");
}

TEST(eof_validation, validate_empty_code)
{
EXPECT_EQ(validate_eof(""), EOFValidationError::invalid_prefix);
Expand Down