Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement non-returning function #672

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 44 additions & 11 deletions lib/evmone/eof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ constexpr auto MAX_STACK_HEIGHT = 0x03FF;
constexpr auto OUTPUTS_INPUTS_NUMBER_LIMIT = 0x7F;
constexpr auto REL_OFFSET_SIZE = sizeof(int16_t);
constexpr auto STACK_SIZE_LIMIT = 1024;
constexpr uint8_t NON_RETURNING_FUNCITON = 0x80;

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

Expand Down Expand Up @@ -195,13 +196,14 @@ std::variant<std::vector<EOFCodeType>, EOFValidationError> validate_types(
container[offset], container[offset + 1], read_uint16_be(&container[offset + 2]));
}

// check 1st section is (0, 0)
if (types[0].inputs != 0 || types[0].outputs != 0)
// check 1st section is (0, 0x80)
if (types[0].inputs != 0 || types[0].outputs != NON_RETURNING_FUNCITON)
return EOFValidationError::invalid_first_section_type;

for (const auto& t : types)
{
if (t.outputs > OUTPUTS_INPUTS_NUMBER_LIMIT || t.inputs > OUTPUTS_INPUTS_NUMBER_LIMIT)
if ((t.outputs > OUTPUTS_INPUTS_NUMBER_LIMIT && t.outputs != NON_RETURNING_FUNCITON) ||
t.inputs > OUTPUTS_INPUTS_NUMBER_LIMIT)
return EOFValidationError::inputs_outputs_num_above_limit;

if (t.max_stack_height > MAX_STACK_HEIGHT)
Expand All @@ -219,6 +221,8 @@ EOFValidationError validate_instructions(

const auto& cost_table = baseline::get_baseline_cost_table(rev, 1);

bool is_returning = false;

for (size_t i = 0; i < code.size(); ++i)
{
const auto op = code[i];
Expand All @@ -235,11 +239,26 @@ EOFValidationError validate_instructions(
if (i >= code.size())
return EOFValidationError::truncated_instruction;
}
else if (op == OP_CALLF || op == OP_JUMPF)
else if (op == OP_CALLF)
{
const auto fid = read_uint16_be(&code[i + 1]);
if (fid >= header.types.size())
return EOFValidationError::invalid_code_section_index;
i += 2;
}
else if (op == OP_RETF)
{
is_returning = true;
static_assert(instr::traits[OP_RETF].immediate_size == 0);
}
else if (op == OP_JUMPF)
{
const auto fid = read_uint16_be(&code[i + 1]);
if (fid >= header.types.size())
return EOFValidationError::invalid_code_section_index;
// JUMPF into returning function means current function is returning.
if (header.types[fid].outputs != NON_RETURNING_FUNCITON)
is_returning = true;
i += 2;
}
else if (op == OP_DATALOADN)
Expand All @@ -253,6 +272,10 @@ EOFValidationError validate_instructions(
i += instr::traits[op].immediate_size;
}

const auto declared_returning = (header.types[code_idx].outputs != NON_RETURNING_FUNCITON);
if (is_returning != declared_returning)
return EOFValidationError::invalid_non_returning_flag;

return EOFValidationError::success;
}

Expand Down Expand Up @@ -357,17 +380,25 @@ std::variant<EOFValidationError, int32_t> validate_max_stack_height(
{
const auto fid = read_uint16_be(&code[i + 1]);

if (code_types[func_index].outputs < code_types[fid].outputs)
return EOFValidationError::jumpf_destination_incompatible_outputs;

if (stack_height + code_types[fid].max_stack_height - code_types[fid].inputs >
STACK_SIZE_LIMIT)
return EOFValidationError::stack_overflow;

stack_height_required = static_cast<int8_t>(
code_types[func_index].outputs + code_types[fid].inputs - code_types[fid].outputs);
if (stack_heights[i] > stack_height_required)
return EOFValidationError::non_empty_stack_on_terminating_instruction;
if (code_types[fid].outputs == NON_RETURNING_FUNCITON)
{
stack_height_required = static_cast<int8_t>(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);
if (stack_heights[i] > stack_height_required)
return EOFValidationError::non_empty_stack_on_terminating_instruction;
}
}

if (stack_height < stack_height_required)
Expand Down Expand Up @@ -649,6 +680,8 @@ std::string_view get_error_message(EOFValidationError err) noexcept
return "invalid_dataloadn_index";
case EOFValidationError::jumpf_destination_incompatible_outputs:
return "jumpf_destination_incompatible_outputs";
case EOFValidationError::invalid_non_returning_flag:
return "invalid_non_returning_flag";
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 @@ -100,6 +100,7 @@ enum class EOFValidationError
invalid_code_section_index,
invalid_dataloadn_index,
jumpf_destination_incompatible_outputs,
invalid_non_returning_flag,

impossible,
};
Expand Down
23 changes: 14 additions & 9 deletions test/unittests/eof_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <evmone/eof.hpp>
#include <gtest/gtest.h>
#include <test/utils/bytecode.hpp>
#include <test/utils/utils.hpp>

using namespace evmone;
Expand Down Expand Up @@ -41,20 +42,24 @@ TEST(eof, read_valid_eof1_header)
for (int i = 0; i < 256; ++i)
section_size_1_256 += "0001";

std::string section_types_256;
for (int i = 0; i < 256; ++i)
section_types_256 += "00800000";

const TestCase test_cases[] = {
{"EF00 01 010004 0200010001 040000 00 00000000 00", 4, 0, {1}},
{"EF00 01 010004 0200010006 040000 00 00000002 600160005500", 4, 0, {6}},
{"EF00 01 010004 0200010001 040001 00 00000000 00 AA", 4, 1, {1}},
{"EF00 01 010004 0200010006 040004 00 00000002 600160005500 AABBCCDD", 4, 4, {6}},
{"EF00 01 01000C 020003000100020003 040000 00 000000000000000000000000 00 5B00 5B5B00", 12,
{"EF00 01 010004 0200010001 040000 00 00800000 00", 4, 0, {1}},
{"EF00 01 010004 0200010006 040000 00 00800002 600160005500", 4, 0, {6}},
{"EF00 01 010004 0200010001 040001 00 00800000 00 AA", 4, 1, {1}},
{"EF00 01 010004 0200010006 040004 00 00800002 600160005500 AABBCCDD", 4, 4, {6}},
{"EF00 01 01000C 020003000100020003 040000 00 008000000080000000800000 00 5B00 5B5B00", 12,
0, {1, 2, 3}},
{"EF00 01 01000C 020003000100020003 040004 00 000000000000000000000000 00 5B00 5B5B00 "
{"EF00 01 01000C 020003000100020003 040004 00 008000000080000000800000 00 5B00 5B5B00 "
"FFFFFFFF",
12, 4, {1, 2, 3}},
{"EF00 01 010004 0200010100 041000 00 00000000" + nops_255 + "00" + std::string(8192, 'F'),
{"EF00 01 010004 0200010100 041000 00 00800000" + nops_255 + "00" + std::string(8192, 'F'),
4, 4096, {256}},
{"EF00 01 010400 020100" + section_size_1_256 + " 041000 00 " +
std::string(4 * 256 * 2, '0') + std::string(512, '0') + std::string(8192, 'F'),
{"EF00 01 010400 020100" + section_size_1_256 + " 041000 00 " + section_types_256 +
std::string(512, '0') + std::string(8192, 'F'),
4 * 256, 4096, std::vector<uint16_t>(256, 1)},
};

Expand Down
Loading