Skip to content

Commit 66dcad5

Browse files
authored
eof: Change max stack height to stack height increase (#1181)
This changes the meaning of the stack height parameter of the code type from "max stack height" (including code inputs) to "stack height increase" (excluding code inputs). The bytecode test unitilty keeps the `max_stack_height` to minimize changes to tests.
1 parent 3b689f5 commit 66dcad5

9 files changed

+47
-47
lines changed

lib/evmone/eof.cpp

+21-19
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ constexpr auto CODE_SECTION_SIZE_SIZE = sizeof(uint16_t);
3131
constexpr auto CONTAINER_SECTION_SIZE_SIZE = sizeof(uint32_t);
3232
constexpr auto CODE_SECTION_NUMBER_LIMIT = 1024;
3333
constexpr auto CONTAINER_SECTION_NUMBER_LIMIT = 256;
34-
constexpr auto MAX_STACK_HEIGHT = 0x03FF;
34+
constexpr auto MAX_STACK_INCREASE_LIMIT = 0x03FF;
3535
constexpr auto OUTPUTS_INPUTS_NUMBER_LIMIT = 0x7F;
3636
constexpr auto REL_OFFSET_SIZE = sizeof(int16_t);
3737
constexpr auto STACK_SIZE_LIMIT = 1024;
@@ -237,7 +237,7 @@ EOFValidationError validate_types(bytes_view container, const EOF1Header& header
237237
{
238238
for (size_t i = 0; i < header.get_type_count(); ++i)
239239
{
240-
const auto [inputs, outputs, max_stack_height] = header.get_type(container, i);
240+
const auto [inputs, outputs, max_stack_increase] = header.get_type(container, i);
241241

242242
// First type should be (0, 0x80)
243243
if (i == 0 && (inputs != 0 || outputs != NON_RETURNING_FUNCTION))
@@ -247,8 +247,8 @@ EOFValidationError validate_types(bytes_view container, const EOF1Header& header
247247
inputs > OUTPUTS_INPUTS_NUMBER_LIMIT)
248248
return EOFValidationError::inputs_outputs_num_above_limit;
249249

250-
if (max_stack_height > MAX_STACK_HEIGHT)
251-
return EOFValidationError::max_stack_height_above_limit;
250+
if (max_stack_increase > MAX_STACK_INCREASE_LIMIT)
251+
return EOFValidationError::max_stack_increase_above_limit;
252252
}
253253

254254
return EOFValidationError::success;
@@ -423,8 +423,11 @@ bool validate_rjump_destinations(bytes_view code) noexcept
423423
return true;
424424
}
425425

426+
/// Validates stack height of the function.
427+
///
426428
/// Requires that the input is validated against truncation.
427-
std::variant<EOFValidationError, int32_t> validate_max_stack_height(
429+
/// Returns computed max stack increase or the validation error.
430+
std::variant<int32_t, EOFValidationError> validate_stack_height(
428431
bytes_view code, size_t func_index, const EOF1Header& header, bytes_view container)
429432
{
430433
// Special value used for detecting errors.
@@ -467,8 +470,7 @@ std::variant<EOFValidationError, int32_t> validate_max_stack_height(
467470
const auto callee_type = header.get_type(container, fid);
468471
stack_height_required = callee_type.inputs;
469472

470-
if (stack_height.max + callee_type.max_stack_height - stack_height_required >
471-
STACK_SIZE_LIMIT)
473+
if (stack_height.max + callee_type.max_stack_increase > STACK_SIZE_LIMIT)
472474
return EOFValidationError::stack_overflow;
473475

474476
// Instruction validation ensures target function is returning
@@ -480,8 +482,7 @@ std::variant<EOFValidationError, int32_t> validate_max_stack_height(
480482
const auto fid = read_uint16_be(&code[i + 1]);
481483
const auto callee_type = header.get_type(container, fid);
482484

483-
if (stack_height.max + callee_type.max_stack_height - callee_type.inputs >
484-
STACK_SIZE_LIMIT)
485+
if (stack_height.max + callee_type.max_stack_increase > STACK_SIZE_LIMIT)
485486
return EOFValidationError::stack_overflow;
486487

487488
if (callee_type.outputs == NON_RETURNING_FUNCTION)
@@ -601,7 +602,8 @@ std::variant<EOFValidationError, int32_t> validate_max_stack_height(
601602

602603
const auto max_stack_height_it = std::ranges::max_element(stack_heights,
603604
[](StackHeightRange lhs, StackHeightRange rhs) noexcept { return lhs.max < rhs.max; });
604-
return max_stack_height_it->max;
605+
const auto max_stack_increase = max_stack_height_it->max - type.inputs;
606+
return max_stack_increase;
605607
}
606608

607609
EOFValidationError validate_eof1(
@@ -684,16 +686,16 @@ EOFValidationError validate_eof1(
684686
return EOFValidationError::invalid_rjump_destination;
685687

686688
// Validate stack
687-
auto msh_or_error = validate_max_stack_height(
689+
const auto shi_or_error = validate_stack_height(
688690
header.get_code(container, code_idx), code_idx, header, container);
689-
if (const auto* error = std::get_if<EOFValidationError>(&msh_or_error))
691+
if (const auto* error = std::get_if<EOFValidationError>(&shi_or_error))
690692
return *error;
691693
// TODO(clang-tidy): Too restrictive, see
692694
// https://github.com/llvm/llvm-project/issues/120867.
693695
// NOLINTNEXTLINE(modernize-use-integer-sign-comparison)
694-
if (std::get<int32_t>(msh_or_error) !=
695-
header.get_type(container, code_idx).max_stack_height)
696-
return EOFValidationError::invalid_max_stack_height;
696+
if (std::get<int32_t>(shi_or_error) !=
697+
header.get_type(container, code_idx).max_stack_increase)
698+
return EOFValidationError::invalid_max_stack_increase;
697699
}
698700

699701
if (std::ranges::find(visited_code_sections, false) != visited_code_sections.end())
@@ -975,10 +977,10 @@ std::string_view get_error_message(EOFValidationError err) noexcept
975977
return "invalid_type_section_size";
976978
case EOFValidationError::invalid_first_section_type:
977979
return "invalid_first_section_type";
978-
case EOFValidationError::invalid_max_stack_height:
979-
return "invalid_max_stack_height";
980-
case EOFValidationError::max_stack_height_above_limit:
981-
return "max_stack_height_above_limit";
980+
case EOFValidationError::invalid_max_stack_increase:
981+
return "invalid_max_stack_increase";
982+
case EOFValidationError::max_stack_increase_above_limit:
983+
return "max_stack_increase_above_limit";
982984
case EOFValidationError::inputs_outputs_num_above_limit:
983985
return "inputs_outputs_num_above_limit";
984986
case EOFValidationError::no_terminating_instruction:

lib/evmone/eof.hpp

+7-7
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ static constexpr auto EOF_CODE_HASH_SENTINEL =
5858

5959
struct EOFCodeType
6060
{
61-
uint8_t inputs; ///< Number of code inputs.
62-
uint8_t outputs; ///< Number of code outputs.
63-
uint16_t max_stack_height; ///< Maximum stack height reached in the code.
61+
uint8_t inputs; ///< Number of code inputs.
62+
uint8_t outputs; ///< Number of code outputs.
63+
uint16_t max_stack_increase; ///< Maximum stack height above the inputs reached in the code.
6464

65-
EOFCodeType(uint8_t inputs_, uint8_t outputs_, uint16_t max_stack_height_)
66-
: inputs{inputs_}, outputs{outputs_}, max_stack_height{max_stack_height_}
65+
EOFCodeType(uint8_t inputs_, uint8_t outputs_, uint16_t max_stack_increase_)
66+
: inputs{inputs_}, outputs{outputs_}, max_stack_increase{max_stack_increase_}
6767
{}
6868
};
6969

@@ -177,11 +177,11 @@ enum class EOFValidationError
177177
too_many_code_sections,
178178
invalid_type_section_size,
179179
invalid_first_section_type,
180-
invalid_max_stack_height,
180+
invalid_max_stack_increase,
181181
no_terminating_instruction,
182182
stack_height_mismatch,
183183
stack_higher_than_outputs_required,
184-
max_stack_height_above_limit,
184+
max_stack_increase_above_limit,
185185
inputs_outputs_num_above_limit,
186186
unreachable_instructions,
187187
stack_underflow,

lib/evmone/instructions.hpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -1105,8 +1105,7 @@ inline code_iterator callf(StackTop stack, ExecutionState& state, code_iterator
11051105
const auto& header = state.analysis.baseline->eof_header();
11061106
const auto stack_size = &stack.top() - state.stack_space.bottom();
11071107
const auto callee_type = header.get_type(state.original_code, index);
1108-
const auto callee_required_stack_size = callee_type.max_stack_height - callee_type.inputs;
1109-
if (stack_size + callee_required_stack_size > StackSpace::limit)
1108+
if (stack_size + callee_type.max_stack_increase > StackSpace::limit)
11101109
{
11111110
state.status = EVMC_STACK_OVERFLOW;
11121111
return nullptr;
@@ -1137,8 +1136,7 @@ inline code_iterator jumpf(StackTop stack, ExecutionState& state, code_iterator
11371136
const auto& header = state.analysis.baseline->eof_header();
11381137
const auto stack_size = &stack.top() - state.stack_space.bottom();
11391138
const auto callee_type = header.get_type(state.original_code, index);
1140-
const auto callee_required_stack_size = callee_type.max_stack_height - callee_type.inputs;
1141-
if (stack_size + callee_required_stack_size > StackSpace::limit)
1139+
if (stack_size + callee_type.max_stack_increase > StackSpace::limit)
11421140
{
11431141
state.status = EVMC_STACK_OVERFLOW;
11441142
return nullptr;

test/unittests/eof_example_test.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,16 @@ TEST_F(state_transition, eof_examples_callf)
9898
// | | |
9999
// version | Header terminator | |
100100
// | |________________ | |_____________ |
101-
"EF00 01 01 0008 02 0002 0006 0001 FF 0000 00 00 80 0001 01 01 0001 602A E30001 00 E4"
101+
"EF00 01 01 0008 02 0002 0006 0001 FF 0000 00 00 80 0001 01 01 0000 602A E30001 00 E4"
102102
// |‾‾‾‾‾‾ |‾‾‾‾‾‾ |‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
103103
// | Header: data section 0 bytes long
104104
// | |
105105
// Header: types section 8 bytes long |
106106
// |
107107
// Types section: first code section 0 inputs,
108-
// non-returning, max stack height 1;
108+
// non-returning, max stack increase 1;
109109
// second code section 1 input,
110-
// 1 output, max stack height 1
110+
// 1 output, max stack increase 0
111111
);
112112

113113
// Tests the code is valid EOF.

test/unittests/eof_validation.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ std::string_view get_tests_error_message(EOFValidationError err) noexcept
5353
return "EOF_InvalidTypeSectionSize";
5454
case EOFValidationError::invalid_first_section_type:
5555
return "EOF_InvalidFirstSectionType";
56-
case EOFValidationError::invalid_max_stack_height:
57-
return "EOF_InvalidMaxStackHeight";
58-
case EOFValidationError::max_stack_height_above_limit:
59-
return "EOF_MaxStackHeightExceeded";
56+
case EOFValidationError::invalid_max_stack_increase:
57+
return "EOFException.INVALID_MAX_STACK_INCREASE";
58+
case EOFValidationError::max_stack_increase_above_limit:
59+
return "EOFException.MAX_STACK_INCREASE_ABOVE_LIMIT";
6060
case EOFValidationError::inputs_outputs_num_above_limit:
6161
return "EOF_InputsOutputsNumAboveLimit";
6262
case EOFValidationError::no_terminating_instruction:

test/unittests/eof_validation_stack_test.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ TEST_F(eof_validation, stack_range_maximally_broad)
7676
add_test_case(eof_bytecode(code, 1023), EOFValidationError::success, "valid_1023_rjumpis");
7777

7878
code = rjumpi(offset, OP_PUSH0) + OP_PUSH0 + code;
79-
add_test_case(eof_bytecode(code, 1023), EOFValidationError::invalid_max_stack_height,
79+
add_test_case(eof_bytecode(code, 1023), EOFValidationError::invalid_max_stack_increase,
8080
"invalid_1024_rjumpis");
8181
}
8282

@@ -882,7 +882,7 @@ TEST_F(eof_validation, underflow_variable_stack)
882882
// JUMPF to returning function - neither min nor max enough for 5 inputs
883883
add_test_case(eof_bytecode(callf(1) + OP_STOP, 3)
884884
.code(varstack + jumpf(2), 0, 3, 3)
885-
.code(bytecode{OP_POP} + OP_POP + OP_RETF, 5, 3, 3),
885+
.code(bytecode{OP_POP} + OP_POP + OP_RETF, 5, 3, 5),
886886
EOFValidationError::stack_underflow);
887887

888888
// JUMPF to non-returning function - [1, 3] stack - neither min nor max enough for 5 inputs
@@ -1181,7 +1181,7 @@ TEST_F(eof_validation, jumpf_to_returning_variable_stack)
11811181
// JUMPF from [1, 3] stack to function with 5 inputs
11821182
add_test_case(eof_bytecode(callf(1) + OP_STOP, 3)
11831183
.code(varstack + jumpf(2), 0, 3, 3)
1184-
.code(push0() + OP_RETF, 5, 3, 3),
1184+
.code(push0() + OP_RETF, 5, 3, 5),
11851185
EOFValidationError::stack_underflow);
11861186

11871187
// JUMPF from [1, 3] stack to function with 3 inputs

test/unittests/eof_validation_test.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -678,19 +678,19 @@ TEST_F(eof_validation, max_stack_height)
678678
EOFValidationError::success);
679679

680680
add_test_case(eof_bytecode(1024 * push(1) + OP_STOP, 1024),
681-
EOFValidationError::max_stack_height_above_limit);
681+
EOFValidationError::max_stack_increase_above_limit);
682682

683683
add_test_case(eof_bytecode(0x400 * push(1) + callf(1) + 0x400 * OP_POP + OP_STOP, 1024)
684684
.code(OP_RETF, 0, 0, 0),
685-
EOFValidationError::max_stack_height_above_limit);
685+
EOFValidationError::max_stack_increase_above_limit);
686686

687687
add_test_case(eof_bytecode(callf(1) + OP_STOP, 0)
688688
.code(0x400 * push(1) + 0x400 * OP_POP + OP_RETF, 0, 0, 1023),
689-
EOFValidationError::invalid_max_stack_height);
689+
EOFValidationError::invalid_max_stack_increase);
690690

691691
add_test_case(eof_bytecode(1024 * push(1) + callf(1) + 1024 * OP_POP + OP_STOP, 1023)
692692
.code(OP_RETF, 0, 0, 0),
693-
EOFValidationError::invalid_max_stack_height);
693+
EOFValidationError::invalid_max_stack_increase);
694694

695695
add_test_case(eof_bytecode(rjumpi(2, 0) + 1 + OP_STOP, 1), EOFValidationError::success);
696696

test/unittests/evm_eof_function_test.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ TEST_P(evm, eof_function_example2)
3030

3131
rev = EVMC_OSAKA;
3232
const auto code =
33-
"ef0001 01000c 020003 003b 0017 001d FF0000 00 00800004 01010003 01010004"
33+
"ef0001 01000c 020003 003b 0017 001d FF0000 00 00800004 01010002 01010003"
3434
"60043560003560e01c63c76652678114e1001c63c6c2ea178114e100065050600080fd50e30002600052602060"
3535
"00f350e3000160005260206000f3"
3636
"60018111e10004506001e460018103e3000181029050e4"

test/utils/bytecode.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ struct eof_bytecode
157157

158158
// types section
159159
for (const auto& type : m_types)
160-
out += bytes{type.inputs, type.outputs} + big_endian(type.max_stack_height);
160+
out += bytes{type.inputs, type.outputs} + big_endian(type.max_stack_increase);
161161
return out;
162162
}
163163

@@ -169,7 +169,7 @@ struct eof_bytecode
169169
auto& code(bytecode c, uint8_t inputs, uint8_t outputs, uint16_t max_stack_height)
170170
{
171171
m_codes.emplace_back(std::move(c));
172-
m_types.emplace_back(inputs, outputs, max_stack_height);
172+
m_types.emplace_back(inputs, outputs, max_stack_height - inputs);
173173
return *this;
174174
}
175175

0 commit comments

Comments
 (0)