Skip to content

Commit

Permalink
DUPN and SWAPN in EOF only + EOF stack validation (#788)
Browse files Browse the repository at this point in the history
  • Loading branch information
pdobacz authored Jan 24, 2024
1 parent 83e7a32 commit efd64c9
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 70 deletions.
2 changes: 1 addition & 1 deletion lib/evmone/advanced_analysis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ struct OpTableEntry
{
instruction_exec_fn fn;
int16_t gas_cost;
int8_t stack_req;
uint8_t stack_req;
int8_t stack_change;
};

Expand Down
8 changes: 8 additions & 0 deletions lib/evmone/advanced_instructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ inline code_iterator impl(AdvancedExecutionState& state, code_iterator pos) noex
state.stack.top_item += instr::traits[Op].stack_height_change;
return new_pos;
}

template <Opcode Op, code_iterator CoreFn(StackTop, code_iterator) noexcept = core::impl<Op>>
inline code_iterator impl(AdvancedExecutionState& state, code_iterator pos) noexcept
{
const auto new_pos = CoreFn(state.stack.top_item, pos);
state.stack.top_item += instr::traits[Op].stack_height_change;
return new_pos;
}
/// @}
} // namespace instr

Expand Down
7 changes: 7 additions & 0 deletions lib/evmone/baseline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ struct Position
return instr_fn(pos.stack_top, state, pos.code_it);
}

[[release_inline]] inline code_iterator invoke(
code_iterator (*instr_fn)(StackTop, code_iterator) noexcept, Position pos, int64_t& /*gas*/,
ExecutionState& /*state*/) noexcept
{
return instr_fn(pos.stack_top, pos.code_it);
}

[[release_inline]] inline code_iterator invoke(
TermResult (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas,
ExecutionState& state) noexcept
Expand Down
2 changes: 2 additions & 0 deletions lib/evmone/baseline_instruction_table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ constexpr auto legacy_cost_tables = []() noexcept {
tables[EVMC_PRAGUE][OP_DATALOADN] = instr::undefined;
tables[EVMC_PRAGUE][OP_DATASIZE] = instr::undefined;
tables[EVMC_PRAGUE][OP_DATACOPY] = instr::undefined;
tables[EVMC_PRAGUE][OP_DUPN] = instr::undefined;
tables[EVMC_PRAGUE][OP_SWAPN] = instr::undefined;
return tables;
}();

Expand Down
17 changes: 10 additions & 7 deletions lib/evmone/eof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ std::variant<EOFValidationError, int32_t> validate_max_stack_height(

const auto opcode = static_cast<Opcode>(code[i]);

auto stack_height_required = instr::traits[opcode].stack_height_required;
int stack_height_required = instr::traits[opcode].stack_height_required;
auto stack_height_change = instr::traits[opcode].stack_height_change;

auto stack_height = stack_heights[i];
Expand All @@ -380,7 +380,7 @@ std::variant<EOFValidationError, int32_t> validate_max_stack_height(
{
const auto fid = read_uint16_be(&code[i + 1]);

stack_height_required = static_cast<int8_t>(code_types[fid].inputs);
stack_height_required = code_types[fid].inputs;

if (stack_height + code_types[fid].max_stack_height - stack_height_required >
STACK_SIZE_LIMIT)
Expand All @@ -401,26 +401,29 @@ std::variant<EOFValidationError, int32_t> validate_max_stack_height(

if (code_types[fid].outputs == NON_RETURNING_FUNCITON)
{
stack_height_required = static_cast<int8_t>(code_types[fid].inputs);
stack_height_required = code_types[fid].inputs;
}
else
{
if (code_types[func_index].outputs < code_types[fid].outputs)
return EOFValidationError::jumpf_destination_incompatible_outputs;

stack_height_required =
static_cast<int8_t>(code_types[func_index].outputs + code_types[fid].inputs -
code_types[fid].outputs);
stack_height_required = code_types[func_index].outputs + code_types[fid].inputs -
code_types[fid].outputs;
if (stack_heights[i] > stack_height_required)
return EOFValidationError::stack_higher_than_outputs_required;
}
}
else if (opcode == OP_RETF)
{
stack_height_required = static_cast<int8_t>(code_types[func_index].outputs);
stack_height_required = code_types[func_index].outputs;
if (stack_height > code_types[func_index].outputs)
return EOFValidationError::stack_higher_than_outputs_required;
}
else if (opcode == OP_DUPN)
stack_height_required = code[i + 1] + 1;
else if (opcode == OP_SWAPN)
stack_height_required = code[i + 1] + 2;

if (stack_height < stack_height_required)
return EOFValidationError::stack_underflow;
Expand Down
30 changes: 4 additions & 26 deletions lib/evmone/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -902,38 +902,16 @@ inline void swap(StackTop stack) noexcept
a[3] = t3;
}

inline code_iterator dupn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept
inline code_iterator dupn(StackTop stack, code_iterator pos) noexcept
{
const auto n = pos[1] + 1;

const auto stack_size = &stack.top() - state.stack_space.bottom();

if (stack_size < n)
{
state.status = EVMC_STACK_UNDERFLOW;
return nullptr;
}

stack.push(stack[n - 1]);

stack.push(stack[pos[1]]);
return pos + 2;
}

inline code_iterator swapn(StackTop stack, ExecutionState& state, code_iterator pos) noexcept
inline code_iterator swapn(StackTop stack, code_iterator pos) noexcept
{
const auto n = pos[1] + 1;

const auto stack_size = &stack.top() - state.stack_space.bottom();

if (stack_size <= n)
{
state.status = EVMC_STACK_UNDERFLOW;
return nullptr;
}

// TODO: This may not be optimal, see instr::core::swap().
std::swap(stack.top(), stack[n]);

std::swap(stack.top(), stack[pos[1] + 1]);
return pos + 2;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/evmone/instructions_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ struct Traits
bool is_terminating = false;

/// The number of stack items the instruction accesses during execution.
int8_t stack_height_required = 0;
uint8_t stack_height_required = 0;

/// The stack height change caused by the instruction execution. Can be negative.
int8_t stack_height_change = 0;
Expand Down
34 changes: 34 additions & 0 deletions test/unittests/eof_validation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,40 @@ TEST(eof_validation, retf_stack_validation)
EXPECT_EQ(validate_eof(code), EOFValidationError::stack_higher_than_outputs_required);
}

TEST(eof_validation, dupn_stack_validation)
{
const auto pushes = 20 * push(1);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "00" + OP_STOP, 21)),
EOFValidationError::success);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "13" + OP_STOP, 21)),
EOFValidationError::success);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "14" + OP_STOP, 21)),
EOFValidationError::stack_underflow);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "d0" + OP_STOP, 21)),
EOFValidationError::stack_underflow);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "fe" + OP_STOP, 21)),
EOFValidationError::stack_underflow);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_DUPN + "ff" + OP_STOP, 21)),
EOFValidationError::stack_underflow);
}

TEST(eof_validation, swapn_stack_validation)
{
const auto pushes = 20 * push(1);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "00" + OP_STOP, 20)),
EOFValidationError::success);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "12" + OP_STOP, 20)),
EOFValidationError::success);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "13" + OP_STOP, 20)),
EOFValidationError::stack_underflow);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "d0" + OP_STOP, 20)),
EOFValidationError::stack_underflow);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "fe" + OP_STOP, 20)),
EOFValidationError::stack_underflow);
EXPECT_EQ(validate_eof(eof_bytecode(pushes + OP_SWAPN + "ff" + OP_STOP, 20)),
EOFValidationError::stack_underflow);
}

TEST(eof_validation, non_returning_status)
{
// Non-returning with no JUMPF and no RETF
Expand Down
72 changes: 37 additions & 35 deletions test/unittests/evm_eip663_dupn_swapn_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,17 @@ TEST_P(evm, dupn)
for (uint64_t i = 1; i <= 20; ++i)
pushes += push(i);

execute(pushes + OP_DUPN + "00" + ret_top());
execute(eof_bytecode(pushes + OP_DUPN + "00" + ret_top(), 22));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(20);

execute(pushes + OP_DUPN + "02" + ret_top());
execute(eof_bytecode(pushes + OP_DUPN + "02" + ret_top(), 22));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(18);

execute(pushes + OP_DUPN + "13" + ret_top());
execute(eof_bytecode(pushes + OP_DUPN + "13" + ret_top(), 22));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(1);

execute(pushes + OP_DUPN + "14" + ret_top());
EXPECT_STATUS(EVMC_STACK_UNDERFLOW);
}

TEST_P(evm, swapn)
Expand All @@ -50,32 +47,29 @@ TEST_P(evm, swapn)
for (uint64_t i = 1; i <= 20; ++i)
pushes += push(i);

execute(pushes + OP_SWAPN + "00" + ret_top());
execute(eof_bytecode(pushes + OP_SWAPN + "00" + ret_top(), 21));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(19);

execute(pushes + OP_SWAPN + "00" + OP_DUPN + "01" + ret_top());
execute(eof_bytecode(pushes + OP_SWAPN + "00" + OP_DUPN + "01" + ret_top(), 22));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(20);

execute(pushes + OP_SWAPN + "01" + ret_top());
execute(eof_bytecode(pushes + OP_SWAPN + "01" + ret_top(), 21));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(18);

execute(pushes + OP_SWAPN + "01" + OP_DUPN + "02" + ret_top());
execute(eof_bytecode(pushes + OP_SWAPN + "01" + OP_DUPN + "02" + ret_top(), 22));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(20);

execute(pushes + OP_SWAPN + "12" + ret_top());
execute(eof_bytecode(pushes + OP_SWAPN + "12" + ret_top(), 21));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(1);

execute(pushes + OP_SWAPN + "12" + OP_DUPN + "13" + ret_top());
execute(eof_bytecode(pushes + OP_SWAPN + "12" + OP_DUPN + "13" + ret_top(), 22));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(20);

execute(pushes + OP_SWAPN + "13" + ret_top());
EXPECT_STATUS(EVMC_STACK_UNDERFLOW);
}

TEST_P(evm, dupn_full_stack)
Expand All @@ -86,23 +80,20 @@ TEST_P(evm, dupn_full_stack)

rev = EVMC_PRAGUE;
auto full_stack_code = bytecode{};
for (uint64_t i = 1023; i >= 1; --i)
for (uint64_t i = 1022; i >= 1; --i)
full_stack_code += push(i);

execute(full_stack_code + OP_POP + OP_DUPN + "00" + ret_top());
execute(eof_bytecode(full_stack_code + OP_POP + OP_DUPN + "00" + ret_top(), 1023));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(2);

execute(full_stack_code + OP_POP + OP_DUPN + "ff" + ret_top());
execute(eof_bytecode(full_stack_code + OP_POP + OP_DUPN + "ff" + ret_top(), 1023));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(257);

execute(full_stack_code + OP_POP + OP_DUPN + "fe" + ret_top());
execute(eof_bytecode(full_stack_code + OP_POP + OP_DUPN + "fe" + ret_top(), 1023));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(256);

execute(full_stack_code + OP_DUPN + "fe" + OP_DUPN + "ff");
EXPECT_STATUS(EVMC_STACK_OVERFLOW);
}

TEST_P(evm, swapn_full_stack)
Expand All @@ -113,22 +104,22 @@ TEST_P(evm, swapn_full_stack)

rev = EVMC_PRAGUE;
auto full_stack_code = bytecode{};
for (uint64_t i = 1024; i >= 1; --i)
for (uint64_t i = 1023; i >= 1; --i)
full_stack_code += push(i);

execute(full_stack_code + OP_POP + OP_SWAPN + "00" + ret_top());
execute(eof_bytecode(full_stack_code + OP_POP + OP_SWAPN + "00" + ret_top(), 1023));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(3);

execute(full_stack_code + OP_POP + OP_SWAPN + "ff" + ret_top());
execute(eof_bytecode(full_stack_code + OP_POP + OP_SWAPN + "ff" + ret_top(), 1023));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(255 + 3);

execute(full_stack_code + OP_POP + OP_SWAPN + "fe" + ret_top());
execute(eof_bytecode(full_stack_code + OP_POP + OP_SWAPN + "fe" + ret_top(), 1023));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(254 + 3);

execute(full_stack_code + OP_SWAPN + "ff" + OP_SWAPN + "00" + OP_RETURN);
execute(eof_bytecode(full_stack_code + OP_SWAPN + "ff" + OP_SWAPN + "00" + OP_RETURN, 1023));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_EQ(result.output_size, 255 + 2);
}
Expand All @@ -144,19 +135,19 @@ TEST_P(evm, dupn_dup_consistency)
for (uint64_t i = 32; i >= 1; --i)
pushes += push(i);

execute(pushes + OP_DUP1 + ret_top());
execute(eof_bytecode(pushes + OP_DUP1 + ret_top(), 34));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(1);

execute(pushes + OP_DUPN + "00" + ret_top());
execute(eof_bytecode(pushes + OP_DUPN + "00" + ret_top(), 34));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(1);

execute(pushes + OP_DUP16 + ret_top());
execute(eof_bytecode(pushes + OP_DUP16 + ret_top(), 34));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(16);

execute(pushes + OP_DUPN + "0f" + ret_top());
execute(eof_bytecode(pushes + OP_DUPN + "0f" + ret_top(), 34));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(16);
}
Expand All @@ -172,19 +163,30 @@ TEST_P(evm, swapn_swap_consistency)
for (uint64_t i = 32; i >= 1; --i)
pushes += push(i);

execute(pushes + OP_SWAP1 + ret_top());
execute(eof_bytecode(pushes + OP_SWAP1 + ret_top(), 33));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(2);

execute(pushes + OP_SWAPN + "00" + ret_top());
execute(eof_bytecode(pushes + OP_SWAPN + "00" + ret_top(), 33));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(2);

execute(pushes + OP_SWAP16 + ret_top());
execute(eof_bytecode(pushes + OP_SWAP16 + ret_top(), 33));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(17);

execute(pushes + OP_SWAPN + "0f" + ret_top());
execute(eof_bytecode(pushes + OP_SWAPN + "0f" + ret_top(), 33));
EXPECT_STATUS(EVMC_SUCCESS);
EXPECT_OUTPUT_INT(17);
}

TEST_P(evm, dupn_swapn_undefined_in_legacy)
{
rev = EVMC_PRAGUE;

execute(push(1) + push(2) + OP_SWAPN + "00");
EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION);

execute(push(1) + OP_DUPN + "00");
EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION);
}

0 comments on commit efd64c9

Please sign in to comment.