Skip to content

Commit

Permalink
Disallow code sections unreachable from section 0
Browse files Browse the repository at this point in the history
  • Loading branch information
hugo-dc committed Apr 23, 2024
1 parent 24a3214 commit 8b5e65a
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 16 deletions.
29 changes: 20 additions & 9 deletions lib/evmone/eof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
#include <limits>
#include <numeric>
#include <ostream>
#include <queue>
#include <span>
#include <stack>
#include <unordered_set>
#include <variant>
#include <vector>

Expand Down Expand Up @@ -215,8 +215,7 @@ std::variant<std::vector<EOFCodeType>, EOFValidationError> validate_types(
}

EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& header,
size_t code_idx, bytes_view container,
std::unordered_set<uint16_t>& accessed_code_sections) noexcept
size_t code_idx, bytes_view container, std::queue<uint16_t>& code_sections_worklist) noexcept
{
const bytes_view code{header.get_code(container, code_idx)};
assert(!code.empty()); // guaranteed by EOF headers validation
Expand Down Expand Up @@ -249,7 +248,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
if (header.types[fid].outputs == NON_RETURNING_FUNCTION)
return EOFValidationError::callf_to_non_returning_function;
if (code_idx != fid)
accessed_code_sections.insert(fid);
code_sections_worklist.push(fid);
i += 2;
}
else if (op == OP_RETF)
Expand All @@ -266,7 +265,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
if (header.types[fid].outputs != NON_RETURNING_FUNCTION)
is_returning = true;
if (code_idx != fid)
accessed_code_sections.insert(fid);
code_sections_worklist.push(fid);
i += 2;
}
else if (op == OP_DATALOADN)
Expand Down Expand Up @@ -551,13 +550,24 @@ std::variant<EOF1Header, EOFValidationError> validate_eof1(
offset += code_size;
}

std::unordered_set<uint16_t> accessed_code_sections = {0};
std::vector<bool> visited_code_sections(code_sizes.size());
std::queue<uint16_t> code_sections_worklist({0});

EOF1Header header{container[2], code_sizes, code_offsets, data_size, types};

for (size_t code_idx = 0; code_idx < header.code_sizes.size(); ++code_idx)
while (!code_sections_worklist.empty())
{
auto code_idx = code_sections_worklist.front();
code_sections_worklist.pop();

if (visited_code_sections[code_idx])
continue;

visited_code_sections[code_idx] = true;

const auto error_instr =
validate_instructions(rev, header, code_idx, container, accessed_code_sections);
validate_instructions(rev, header, code_idx, container, code_sections_worklist);

if (error_instr != EOFValidationError::success)
return error_instr;

Expand All @@ -572,7 +582,8 @@ std::variant<EOF1Header, EOFValidationError> validate_eof1(
return EOFValidationError::invalid_max_stack_height;
}

if (accessed_code_sections.size() != header.code_sizes.size())
if (std::find(visited_code_sections.begin(), visited_code_sections.end(), false) !=
visited_code_sections.end())
return EOFValidationError::unreachable_code_sections;

return header;
Expand Down
29 changes: 22 additions & 7 deletions test/unittests/eof_validation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -629,8 +629,8 @@ TEST_F(eof_validation, max_stack_height)
0x400 * OP_POP + OP_STOP + OP_RETF,
EOFValidationError::max_stack_height_above_limit);

add_test_case(bytecode{"EF0001 010008 02000200010C01 040000 00 00800000 000003FF"} + OP_STOP +
0x400 * bytecode{1} + 0x400 * OP_POP + OP_RETF,
add_test_case(eof_bytecode(callf(1) + OP_STOP, 0)
.code(0x400 * bytecode{1} + 0x400 * OP_POP + OP_RETF, 0, 0, 1023),
EOFValidationError::invalid_max_stack_height);

add_test_case("EF0001 010008 0200020C010001 040000 00 008003FF 00000000" + 0x400 * bytecode{1} +
Expand Down Expand Up @@ -796,23 +796,24 @@ TEST_F(eof_validation, non_returning_status)
EOFValidationError::success);

// Invalid with RETF
add_test_case("EF0001 010008 02000200010001 040000 00 0080000000800000 00 E4",
add_test_case(eof_bytecode(jumpf(1)).code(OP_RETF, 0, 0x80, 0),
EOFValidationError::invalid_non_returning_flag);
add_test_case("EF0001 010004 0200010001 040000 00 00800000 E4",
EOFValidationError::invalid_non_returning_flag);
// Invalid with JUMPF to returning
add_test_case(
"EF0001 01000c 020003000100030001 040000 00 008000000080000000000000 00 E50002 E4",
"EF0001 01000c 020003000300030001 040000 00 008000000080000000000000 E50001 E50002 E4",
EOFValidationError::invalid_non_returning_flag);
// Invalid with JUMPF to non-returning
add_test_case("EF0001 010008 02000200010003 040000 00 0080000000000000 00 E50000",
add_test_case("EF0001 010008 02000200030003 040000 00 0080000000000000 E50001 E50000",
EOFValidationError::invalid_non_returning_flag);
// Invalid with JUMPF to returning and RETF
add_test_case(
"EF0001 01000C 020003000100070001 040000 00 008000000180000100000000 00 E10001E4E50002 E4",
"EF0001 01000C 020003000400070001 040000 00 008000010180000100000000 5FE50001 "
"E10001E4E50002 E4",
EOFValidationError::invalid_non_returning_flag);
// Invalid with JUMPF to non-returning and RETF
add_test_case("EF0001 010008 02000200010007 040000 00 0080000001800001 00 E10001E4E50000",
add_test_case("EF0001 010008 02000200040007 040000 00 0080000101800001 5FE50001 E10001E4E50000",
EOFValidationError::invalid_non_returning_flag);

// Circular JUMPF: can be both returning and non-returning
Expand Down Expand Up @@ -909,5 +910,19 @@ TEST_F(eof_validation, unreachable_code_sections)
// Code Section 254 calls itself instead of code section 255, leaving code section 255
// unreachable
add_test_case(code_sections_256_err_254, EOFValidationError::unreachable_code_sections);

// Code Section 0 calls section 1, which calls itself, leaving section
// 2 unreachable
add_test_case(eof_bytecode(jumpf(1)).code(jumpf(1), 0, 0x80, 0).code(jumpf(2), 0, 0x80, 0),
EOFValidationError::unreachable_code_sections);

// Code Section 0 calls section 1, which calls section 2, section 3 and
// 4 call each other but are not reachable from section 0
add_test_case(eof_bytecode(jumpf(1))
.code(jumpf(2), 0, 0x80, 0)
.code(OP_INVALID, 0, 0x80, 0)
.code(jumpf(4), 0, 0x80, 0)
.code(jumpf(3), 0, 0x80, 0),
EOFValidationError::unreachable_code_sections);
}
}

0 comments on commit 8b5e65a

Please sign in to comment.