Skip to content

Commit

Permalink
EOF: limit validated container size to MAX_INITCODE_SIZE (#930)
Browse files Browse the repository at this point in the history
  • Loading branch information
pdobacz authored Jun 26, 2024
1 parent 3cab4ca commit dbf8fb4
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 4 deletions.
8 changes: 6 additions & 2 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ commands:

download_execution_spec_tests:
parameters:
repo:
type: string
default: ethereum/execution-spec-tests
release:
type: string
fixtures_suffix:
Expand All @@ -139,7 +142,7 @@ commands:
name: "Download execution-spec-tests: <<parameters.release>>"
working_directory: ~/spec-tests
command: |
curl -L https://github.com/ethereum/execution-spec-tests/releases/download/<<parameters.release>>/fixtures_<<parameters.fixtures_suffix>>.tar.gz | tar -xz
curl -L https://github.com/<<parameters.repo>>/releases/download/<<parameters.release>>/fixtures_<<parameters.fixtures_suffix>>.tar.gz | tar -xz
ls -l
build:
Expand Down Expand Up @@ -398,7 +401,8 @@ jobs:
steps:
- build
- download_execution_spec_tests:
release: [email protected]
repo: ipsilon/execution-spec-tests
release: [email protected]+ipsilon_bf37249b
fixtures_suffix: eip7692
- run:
name: "EOF pre-release execution spec tests (state_tests)"
Expand Down
19 changes: 18 additions & 1 deletion lib/evmone/eof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ constexpr auto REL_OFFSET_SIZE = sizeof(int16_t);
constexpr auto STACK_SIZE_LIMIT = 1024;
constexpr uint8_t NON_RETURNING_FUNCTION = 0x80;

constexpr size_t MAX_CODE_SIZE = 0x6000;
constexpr size_t MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE;

using EOFSectionHeaders = std::array<std::vector<uint16_t>, MAX_SECTION + 1>;

size_t eof_header_size(const EOFSectionHeaders& headers) noexcept
Expand Down Expand Up @@ -592,6 +595,10 @@ EOFValidationError validate_eof1(
bytes_view bytes;
ContainerKind kind;
};

if (main_container.size() > MAX_INITCODE_SIZE)
return EOFValidationError::container_size_above_limit;

// Queue of containers left to process
std::queue<ContainerValidation> container_queue;

Expand Down Expand Up @@ -738,6 +745,12 @@ std::variant<EOF1Header, EOFValidationError> validate_header(
if (rev < EVMC_PRAGUE)
return EOFValidationError::eof_version_unknown;

// `offset` variable handled below is known to not be greater than the container size, as
// checked in `validate_section_headers`. Combined with the requirement for the container
// size to not exceed MAX_INITCODE_SIZE (checked before `validate-header` is called),
// this allows us to cast `offset` to narrower integers.
assert(container.size() <= MAX_INITCODE_SIZE);

const auto section_headers_or_error = validate_section_headers(container);
if (const auto* error = std::get_if<EOFValidationError>(&section_headers_or_error))
return *error;
Expand All @@ -757,6 +770,7 @@ std::variant<EOF1Header, EOFValidationError> validate_header(
std::vector<uint16_t> 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<uint16_t>::max());
Expand All @@ -768,10 +782,11 @@ std::variant<EOF1Header, EOFValidationError> validate_header(
std::vector<uint16_t> container_offsets;
for (const auto container_size : container_sizes)
{
assert(offset <= std::numeric_limits<uint16_t>::max());
container_offsets.emplace_back(static_cast<uint16_t>(offset));
offset += container_size;
}
// NOTE: assertion always satisfied only as long as initcode limits apply (48K).

assert(offset <= std::numeric_limits<uint16_t>::max());
const auto data_offset = static_cast<uint16_t>(offset);

Expand Down Expand Up @@ -971,6 +986,8 @@ std::string_view get_error_message(EOFValidationError err) noexcept
return "ambiguous_container_kind";
case EOFValidationError::incompatible_container_kind:
return "incompatible_container_kind";
case EOFValidationError::container_size_above_limit:
return "container_size_above_limit";
case EOFValidationError::impossible:
return "impossible";
}
Expand Down
1 change: 1 addition & 0 deletions lib/evmone/eof.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ enum class EOFValidationError
toplevel_container_truncated,
ambiguous_container_kind,
incompatible_container_kind,
container_size_above_limit,

impossible,
};
Expand Down
2 changes: 2 additions & 0 deletions test/unittests/eof_validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ std::string_view get_tests_error_message(EOFValidationError err) noexcept
return "EOF_AmbiguousContainerKind";
case EOFValidationError::incompatible_container_kind:
return "EOF_IncompatibleContainerKind";
case EOFValidationError::container_size_above_limit:
return "EOF_ContainerSizeAboveLimit";
case EOFValidationError::impossible:
return "impossible";
}
Expand Down
4 changes: 3 additions & 1 deletion test/unittests/eof_validation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1211,9 +1211,11 @@ TEST_F(eof_validation, EOF1_subcontainer_containing_unreachable_code_sections)

TEST_F(eof_validation, max_nested_containers)
{
constexpr size_t MAX_CODE_SIZE = 0x6000;
constexpr size_t MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE;
bytecode code{};
bytecode nextcode = eof_bytecode(OP_INVALID);
while (nextcode.size() <= std::numeric_limits<uint16_t>::max())
while (nextcode.size() <= MAX_INITCODE_SIZE)
{
code = nextcode;
nextcode = eof_bytecode(OP_INVALID).container(nextcode);
Expand Down

0 comments on commit dbf8fb4

Please sign in to comment.