Skip to content

Commit

Permalink
Use std::variant to report errors in EOF validation
Browse files Browse the repository at this point in the history
This helps to reduce stack usage of large functions like validate_eof_headers().
  • Loading branch information
gumb0 authored and axic committed Feb 20, 2023
1 parent 4971df8 commit 6acd9d3
Showing 1 changed file with 54 additions and 46 deletions.
100 changes: 54 additions & 46 deletions lib/evmone/eof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <limits>
#include <numeric>
#include <stack>
#include <variant>
#include <vector>

namespace evmone
Expand Down Expand Up @@ -43,7 +44,7 @@ size_t eof_header_size(const EOFSectionHeaders& headers) noexcept
code_section_count * code_section_size_size + sizeof(TERMINATOR);
}

std::pair<EOFSectionHeaders, EOFValidationError> validate_eof_headers(bytes_view container)
std::variant<EOFSectionHeaders, EOFValidationError> validate_eof_headers(bytes_view container)
{
enum class State
{
Expand Down Expand Up @@ -71,51 +72,51 @@ std::pair<EOFSectionHeaders, EOFValidationError> validate_eof_headers(bytes_view
{
case TERMINATOR:
if (section_headers[TYPE_SECTION].empty())
return {{}, EOFValidationError::type_section_missing};
return EOFValidationError::type_section_missing;
if (section_headers[CODE_SECTION].empty())
return {{}, EOFValidationError::code_section_missing};
return EOFValidationError::code_section_missing;
if (section_headers[DATA_SECTION].empty())
return {{}, EOFValidationError::data_section_missing};
return EOFValidationError::data_section_missing;
state = State::terminated;
break;
case TYPE_SECTION:
if (!section_headers[TYPE_SECTION].empty())
return {{}, EOFValidationError::multiple_type_sections};
return EOFValidationError::multiple_type_sections;
if (!section_headers[CODE_SECTION].empty())
return {{}, EOFValidationError::code_section_before_type_section};
return EOFValidationError::code_section_before_type_section;
state = State::section_size;
break;
case DATA_SECTION:
if (section_headers[TYPE_SECTION].empty())
return {{}, EOFValidationError::data_section_before_types_section};
return EOFValidationError::data_section_before_types_section;
if (section_headers[CODE_SECTION].empty())
return {{}, EOFValidationError::data_section_before_code_section};
return EOFValidationError::data_section_before_code_section;
if (!section_headers[DATA_SECTION].empty())
return {{}, EOFValidationError::multiple_data_sections};
return EOFValidationError::multiple_data_sections;
state = State::section_size;
break;
case CODE_SECTION:
{
if (section_headers[TYPE_SECTION].empty())
return {{}, EOFValidationError::code_section_before_type_section};
return EOFValidationError::code_section_before_type_section;
if (!section_headers[DATA_SECTION].empty())
return {{}, EOFValidationError::data_section_before_code_section};
return EOFValidationError::data_section_before_code_section;
if (!section_headers[CODE_SECTION].empty())
return {{}, EOFValidationError::multiple_code_sections_headers};
return EOFValidationError::multiple_code_sections_headers;
if (it == container_end)
return {{}, EOFValidationError::incomplete_section_number};
return EOFValidationError::incomplete_section_number;
const auto section_number_hi = *it++;
if (it == container_end)
return {{}, EOFValidationError::incomplete_section_number};
return EOFValidationError::incomplete_section_number;
const auto section_number_lo = *it++;
section_num = static_cast<uint16_t>((section_number_hi << 8) | section_number_lo);
if (section_num == 0)
return {{}, EOFValidationError::zero_section_size};
return EOFValidationError::zero_section_size;
state = State::section_size;
break;
}
default:
return {{}, EOFValidationError::unknown_section_id};
return EOFValidationError::unknown_section_id;
}
break;
}
Expand All @@ -127,29 +128,29 @@ std::pair<EOFSectionHeaders, EOFValidationError> validate_eof_headers(bytes_view
for (size_t i = 0; i < section_num; ++i)
{
if (it == container_end)
return {{}, EOFValidationError::incomplete_section_size};
return EOFValidationError::incomplete_section_size;
const auto size_hi = *it++;
if (it == container_end)
return {{}, EOFValidationError::incomplete_section_size};
return EOFValidationError::incomplete_section_size;
const auto size_lo = *it++;
const auto section_size = static_cast<uint16_t>((size_hi << 8) | size_lo);
if (section_size == 0)
return {{}, EOFValidationError::zero_section_size};
return EOFValidationError::zero_section_size;

if (section_headers[CODE_SECTION].size() == CODE_SECTION_NUMBER_LIMIT)
return {{}, EOFValidationError::too_many_code_sections};
return EOFValidationError::too_many_code_sections;
section_headers[section_id].emplace_back(section_size);
}
}
else // TYPES_SECTION or DATA_SECTION
{
const auto size_hi = *it++;
if (it == container_end)
return {{}, EOFValidationError::incomplete_section_size};
return EOFValidationError::incomplete_section_size;
const auto size_lo = *it++;
const auto section_size = static_cast<uint16_t>((size_hi << 8) | size_lo);
if (section_size == 0 && section_id != DATA_SECTION)
return {{}, EOFValidationError::zero_section_size};
return EOFValidationError::zero_section_size;

section_headers[section_id].emplace_back(section_size);
}
Expand All @@ -158,12 +159,12 @@ std::pair<EOFSectionHeaders, EOFValidationError> validate_eof_headers(bytes_view
break;
}
case State::terminated:
return {{}, EOFValidationError::impossible};
return EOFValidationError::impossible;
}
}

if (state != State::terminated)
return {{}, EOFValidationError::section_headers_not_terminated};
return EOFValidationError::section_headers_not_terminated;

const auto section_bodies_size =
(!section_headers[TYPE_SECTION].empty() ? section_headers[TYPE_SECTION].front() : 0) +
Expand All @@ -172,23 +173,23 @@ std::pair<EOFSectionHeaders, EOFValidationError> validate_eof_headers(bytes_view
(!section_headers[DATA_SECTION].empty() ? section_headers[DATA_SECTION].front() : 0);
const auto remaining_container_size = container_end - it;
if (section_bodies_size != remaining_container_size)
return {{}, EOFValidationError::invalid_section_bodies_size};
return EOFValidationError::invalid_section_bodies_size;

if (!section_headers[TYPE_SECTION].empty() &&
section_headers[TYPE_SECTION][0] != section_headers[CODE_SECTION].size() * 4)
return {{}, EOFValidationError::invalid_type_section_size};
return EOFValidationError::invalid_type_section_size;

return {section_headers, EOFValidationError::success};
return section_headers;
}

std::pair<std::vector<EOF1TypeHeader>, EOFValidationError> validate_types(
std::variant<std::vector<EOF1TypeHeader>, EOFValidationError> validate_types(
bytes_view container, size_t header_size, std::vector<uint16_t> type_section_sizes) noexcept
{
assert(!container.empty()); // guaranteed by EOF headers validation
assert(type_section_sizes.size() <= 1); // guaranteed by EOF headers validation

if (type_section_sizes.empty())
return {{{0, 0, 0}}, EOFValidationError::success};
return std::vector{EOF1TypeHeader{0, 0, 0}};

std::vector<EOF1TypeHeader> types;

Expand All @@ -203,19 +204,19 @@ std::pair<std::vector<EOF1TypeHeader>, EOFValidationError> validate_types(

// check 1st section is (0, 0)
if (types[0].inputs_num != 0 || types[0].outputs_num != 0)
return {{}, EOFValidationError::invalid_first_section_type};
return EOFValidationError::invalid_first_section_type;

for (const auto& t : types)
{
if (t.max_stack_height > MAX_STACK_HEIGHT)
return {{}, EOFValidationError::max_stack_height_above_limit};
return EOFValidationError::max_stack_height_above_limit;

if (t.outputs_num > OUTPUTS_INPUTS_NUMBER_LIMIT ||
t.inputs_num > OUTPUTS_INPUTS_NUMBER_LIMIT)
return {{}, EOFValidationError::inputs_outputs_num_above_limit};
return EOFValidationError::inputs_outputs_num_above_limit;
}

return {types, EOFValidationError::success};
return types;
}

EOFValidationError validate_instructions(evmc_revision rev, bytes_view code) noexcept
Expand Down Expand Up @@ -444,23 +445,25 @@ std::pair<EOFValidationError, int32_t> validate_max_stack_height(
return {EOFValidationError::success, *msh_it};
}

std::pair<EOF1Header, EOFValidationError> validate_eof1(
std::variant<EOF1Header, EOFValidationError> validate_eof1(
evmc_revision rev, bytes_view container) noexcept
{
const auto [section_headers, error_header] = validate_eof_headers(container);
if (error_header != EOFValidationError::success)
return {{}, error_header};
const auto section_headers_or_error = validate_eof_headers(container);
if (const auto* error = std::get_if<EOFValidationError>(&section_headers_or_error))
return *error;

const auto& section_headers = std::get<EOFSectionHeaders>(section_headers_or_error);
const auto& code_sizes = section_headers[CODE_SECTION];
const auto data_size =
section_headers[DATA_SECTION].empty() ? uint16_t{0} : section_headers[DATA_SECTION][0];

const auto header_size = eof_header_size(section_headers);

const auto [types, error_types] =
const auto types_or_error =
validate_types(container, header_size, section_headers[TYPE_SECTION]);
if (error_types != EOFValidationError::success)
return {{}, error_types};
if (const auto* error = std::get_if<EOFValidationError>(&types_or_error))
return *error;
const auto& types = std::get<std::vector<EOF1TypeHeader>>(types_or_error);

std::vector<uint16_t> code_offsets;
const auto type_section_size =
Expand All @@ -479,21 +482,21 @@ std::pair<EOF1Header, EOFValidationError> validate_eof1(
const auto error_instr = validate_instructions(
rev, {&container[header.code_begin(code_idx)], header.code_sizes[code_idx]});
if (error_instr != EOFValidationError::success)
return {{}, error_instr};
return error_instr;

if (!validate_rjump_destinations(header, code_idx, container.begin()))
return {{}, EOFValidationError::invalid_rjump_destination};
return EOFValidationError::invalid_rjump_destination;

auto msh_validation_result = validate_max_stack_height(
{&container[header.code_begin(code_idx)], header.code_sizes[code_idx]}, code_idx,
header.types);
if (msh_validation_result.first != EOFValidationError::success)
return {{}, msh_validation_result.first};
return msh_validation_result.first;
if (msh_validation_result.second != header.types[code_idx].max_stack_height)
return {{}, EOFValidationError::invalid_max_stack_height};
return EOFValidationError::invalid_max_stack_height;
}

return {header, EOFValidationError::success};
return header;
}

} // namespace
Expand Down Expand Up @@ -597,7 +600,12 @@ EOFValidationError validate_eof(evmc_revision rev, bytes_view container) noexcep
{
if (rev < EVMC_CANCUN)
return EOFValidationError::eof_version_unknown;
return validate_eof1(rev, container).second;

const auto header_or_error = validate_eof1(rev, container);
if (const auto* error = std::get_if<EOFValidationError>(&header_or_error))
return *error;
else
return EOFValidationError::success;
}
else
return EOFValidationError::eof_version_unknown;
Expand Down

0 comments on commit 6acd9d3

Please sign in to comment.