Skip to content

Commit f835186

Browse files
authored
Merge pull request #594 from ethereum/analyze-eof-refactor
Enable stack height validation for CALLF
2 parents fd018f8 + 2dac2d7 commit f835186

File tree

6 files changed

+97
-26
lines changed

6 files changed

+97
-26
lines changed

lib/evmone/baseline.cpp

+3-13
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ CodeAnalysis analyze_legacy(bytes_view code)
6969

7070
CodeAnalysis analyze_eof1(bytes_view container)
7171
{
72-
const auto header = read_valid_eof1_header(container);
72+
auto header = read_valid_eof1_header(container);
7373

7474
// Extract all code sections as single buffer reference.
7575
// TODO: It would be much easier if header had code_sections_offset and data_section_offset
@@ -79,17 +79,7 @@ CodeAnalysis analyze_eof1(bytes_view container)
7979
const auto executable_code =
8080
container.substr(code_sections_offset, code_sections_end - code_sections_offset);
8181

82-
// Code section offsets relative to the beginning of the first code section.
83-
// Execution starts at position 0 (first code section).
84-
// The implementation of CALLF uses these offsets.
85-
CodeAnalysis::CodeOffsets relative_offsets;
86-
relative_offsets.reserve(header.code_offsets.size());
87-
for (const auto offset : header.code_offsets)
88-
relative_offsets.push_back(offset - code_sections_offset);
89-
90-
// FIXME: Better way of getting EOF version.
91-
const auto eof_version = container[2];
92-
return CodeAnalysis{executable_code, {}, eof_version, relative_offsets};
82+
return CodeAnalysis{executable_code, std::move(header)};
9383
}
9484
} // namespace
9585

@@ -338,7 +328,7 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana
338328

339329
const auto code = analysis.executable_code;
340330

341-
const auto& cost_table = get_baseline_cost_table(state.rev, analysis.eof_version);
331+
const auto& cost_table = get_baseline_cost_table(state.rev, analysis.eof_header.version);
342332

343333
auto* tracer = vm.get_tracer();
344334
if (INTX_UNLIKELY(tracer != nullptr))

lib/evmone/baseline.hpp

+4-10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// SPDX-License-Identifier: Apache-2.0
44
#pragma once
55

6+
#include "eof.hpp"
67
#include <evmc/evmc.h>
78
#include <evmc/utils.h>
89
#include <memory>
@@ -22,14 +23,10 @@ class CodeAnalysis
2223
{
2324
public:
2425
using JumpdestMap = std::vector<bool>;
25-
using CodeOffsets = std::vector<uint16_t>;
2626

2727
bytes_view executable_code; ///< Executable code section.
2828
JumpdestMap jumpdest_map; ///< Map of valid jump destinations.
29-
uint8_t eof_version = 0; ///< The EOF version, 0 means legacy code.
30-
/// Offset of each code section relative to the beginning of the first code
31-
/// section. We flatten the sections for cheap execution.
32-
CodeOffsets code_offsets;
29+
EOF1Header eof_header; ///< The EOF header.
3330

3431
private:
3532
/// Padded code for faster legacy code execution.
@@ -43,11 +40,8 @@ class CodeAnalysis
4340
m_padded_code{std::move(padded_code)}
4441
{}
4542

46-
CodeAnalysis(bytes_view code, JumpdestMap map, uint8_t version, CodeOffsets offsets)
47-
: executable_code{code},
48-
jumpdest_map{std::move(map)},
49-
eof_version{version},
50-
code_offsets{std::move(offsets)}
43+
CodeAnalysis(bytes_view code, EOF1Header header)
44+
: executable_code{code}, eof_header{std::move(header)}
5145
{}
5246
};
5347
static_assert(std::is_move_constructible_v<CodeAnalysis>);

lib/evmone/eof.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ std::variant<EOF1Header, EOFValidationError> validate_eof1(
430430
offset += code_size;
431431
}
432432

433-
EOF1Header header{code_sizes, code_offsets, data_size, types};
433+
EOF1Header header{container[2], code_sizes, code_offsets, data_size, types};
434434

435435
for (size_t code_idx = 0; code_idx < header.code_sizes.size(); ++code_idx)
436436
{
@@ -489,6 +489,8 @@ EOF1Header read_valid_eof1_header(bytes_view container)
489489

490490
EOF1Header header;
491491

492+
header.version = container[2];
493+
492494
for (auto type_offset = header_size;
493495
type_offset < header_size + section_headers[TYPE_SECTION][0]; type_offset += 4)
494496
{

lib/evmone/eof.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ struct EOFCodeType
2828

2929
struct EOF1Header
3030
{
31+
/// The EOF version, 0 means legacy code.
32+
uint8_t version = 0;
33+
3134
/// Size of every code section.
3235
std::vector<uint16_t> code_sizes;
3336

lib/evmone/instructions.hpp

+17-2
Original file line numberDiff line numberDiff line change
@@ -933,11 +933,26 @@ evmc_status_code create_impl(StackTop stack, ExecutionState& state) noexcept;
933933
inline constexpr auto create = create_impl<OP_CREATE>;
934934
inline constexpr auto create2 = create_impl<OP_CREATE2>;
935935

936-
inline code_iterator callf(StackTop /*stack*/, ExecutionState& state, code_iterator pos) noexcept
936+
inline code_iterator callf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept
937937
{
938938
const auto index = read_uint16_be(&pos[1]);
939+
const auto& header = state.analysis.baseline->eof_header;
940+
const auto stack_size = &stack.top() - state.stack_space.bottom();
941+
if (stack_size + header.types[index].max_stack_height > StackSpace::limit)
942+
{
943+
state.status = EVMC_STACK_OVERFLOW;
944+
return nullptr;
945+
}
946+
947+
if (state.call_stack.size() >= StackSpace::limit)
948+
{
949+
// TODO: Add different error code.
950+
state.status = EVMC_STACK_OVERFLOW;
951+
return nullptr;
952+
}
939953
state.call_stack.push_back(pos + 3);
940-
const auto offset = state.analysis.baseline->code_offsets[index];
954+
955+
const auto offset = header.code_offsets[index] - header.code_offsets[0];
941956
auto code = state.analysis.baseline->executable_code;
942957
return code.data() + offset;
943958
}

test/unittests/evm_eof_function_test.cpp

+67
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,70 @@ TEST_P(evm, eof_function_example2)
5757
EXPECT_GAS_USED(EVMC_SUCCESS, 44544);
5858
EXPECT_EQ(output, "0000000000000000000000000000000000000000000000000000000000000262"_hex);
5959
}
60+
61+
TEST_P(evm, callf_stack_size_1024)
62+
{
63+
// CALLF is not implemented in Advanced.
64+
if (is_advanced())
65+
return;
66+
67+
rev = EVMC_CANCUN;
68+
const auto code = bytecode{"ef0001 010008 020002 0BFF 0004 030000 00 000003FF 00000001"_hex} +
69+
1023 * push(1) + OP_CALLF + bytecode{"0x0001"_hex} + 1021 * OP_POP +
70+
OP_RETURN + push(1) + OP_POP + OP_RETF;
71+
72+
ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success);
73+
execute(bytecode{code});
74+
EXPECT_STATUS(EVMC_SUCCESS);
75+
}
76+
77+
TEST_P(evm, callf_stack_overflow)
78+
{
79+
// CALLF is not implemented in Advanced.
80+
if (is_advanced())
81+
return;
82+
83+
rev = EVMC_CANCUN;
84+
const auto code =
85+
bytecode{"ef0001 010008 020002 0BFF 0007 030000 00 000003FF 00000002"_hex} + // EOF header
86+
1023 * push(1) + OP_CALLF + bytecode{"0x0001"_hex} + 1021 * OP_POP + OP_RETURN +
87+
2 * push(1) + 2 * OP_POP + OP_RETF;
88+
89+
ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success);
90+
execute(bytecode{code});
91+
EXPECT_STATUS(EVMC_STACK_OVERFLOW);
92+
}
93+
94+
TEST_P(evm, callf_call_stack_size_1024)
95+
{
96+
// CALLF is not implemented in Advanced.
97+
if (is_advanced())
98+
return;
99+
100+
rev = EVMC_CANCUN;
101+
const auto code = bytecode{"ef0001 010008 020002 0007 000e 030000 00 00000001 01000002"_hex} +
102+
push(1023) + OP_CALLF + bytecode{"0x0001"_hex} + OP_STOP + OP_DUP1 +
103+
OP_RJUMPI + bytecode{"0x0002"_hex} + OP_POP + OP_RETF + push(1) + OP_SWAP1 +
104+
OP_SUB + OP_CALLF + bytecode{"0x0001"_hex} + OP_RETF;
105+
106+
ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success);
107+
execute(bytecode{code});
108+
EXPECT_STATUS(EVMC_SUCCESS);
109+
}
110+
111+
TEST_P(evm, callf_call_stack_size_1025)
112+
{
113+
// CALLF is not implemented in Advanced.
114+
if (is_advanced())
115+
return;
116+
117+
rev = EVMC_CANCUN;
118+
const auto code = bytecode{"ef0001 010008 020002 0007 000e 030000 00 00000001 01000002"_hex} +
119+
push(1024) + OP_CALLF + bytecode{"0x0001"_hex} + OP_STOP + OP_DUP1 +
120+
OP_RJUMPI + bytecode{"0x0002"_hex} + OP_POP + OP_RETF + push(1) + OP_SWAP1 +
121+
OP_SUB + OP_CALLF + bytecode{"0x0001"_hex} + OP_RETF;
122+
123+
ASSERT_EQ(evmone::validate_eof(rev, code), evmone::EOFValidationError::success);
124+
execute(bytecode{code});
125+
EXPECT_STATUS(EVMC_STACK_OVERFLOW);
126+
}

0 commit comments

Comments
 (0)