Skip to content

Commit 41cc722

Browse files
authored
Disallow code sections unreachable from section 0 (#866)
- Validates the code by using a worklist of accessed code sections - At the end if any code section was not accessed will return unreachable_code_sections error. - Fixes tests previously containing unreachable code sections
2 parents 1b2d6b5 + e8626d2 commit 41cc722

File tree

2 files changed

+56
-15
lines changed

2 files changed

+56
-15
lines changed

lib/evmone/eof.cpp

+19-8
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
#include <limits>
1515
#include <numeric>
1616
#include <ostream>
17+
#include <queue>
1718
#include <span>
1819
#include <stack>
19-
#include <unordered_set>
2020
#include <variant>
2121
#include <vector>
2222

@@ -251,7 +251,7 @@ std::variant<std::vector<EOFCodeType>, EOFValidationError> validate_types(
251251

252252
EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& header,
253253
std::span<const EOF1Header> subcontainer_headers, size_t code_idx, bytes_view container,
254-
std::unordered_set<uint16_t>& accessed_code_sections) noexcept
254+
std::queue<uint16_t>& code_sections_worklist) noexcept
255255
{
256256
const bytes_view code{header.get_code(container, code_idx)};
257257
assert(!code.empty()); // guaranteed by EOF headers validation
@@ -284,7 +284,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
284284
if (header.types[fid].outputs == NON_RETURNING_FUNCTION)
285285
return EOFValidationError::callf_to_non_returning_function;
286286
if (code_idx != fid)
287-
accessed_code_sections.insert(fid);
287+
code_sections_worklist.push(fid);
288288
i += 2;
289289
}
290290
else if (op == OP_RETF)
@@ -301,7 +301,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
301301
if (header.types[fid].outputs != NON_RETURNING_FUNCTION)
302302
is_returning = true;
303303
if (code_idx != fid)
304-
accessed_code_sections.insert(fid);
304+
code_sections_worklist.push(fid);
305305
i += 2;
306306
}
307307
else if (op == OP_DATALOADN)
@@ -611,7 +611,8 @@ std::variant<EOF1Header, EOFValidationError> validate_eof1( // NOLINT(misc-no-r
611611
}
612612
const auto data_offset = static_cast<uint16_t>(offset);
613613

614-
std::unordered_set<uint16_t> accessed_code_sections = {0};
614+
std::vector<bool> visited_code_sections(code_sizes.size());
615+
std::queue<uint16_t> code_sections_worklist({0});
615616
EOF1Header header{container[2], code_sizes, code_offsets, data_size, data_offset,
616617
container_sizes, container_offsets, types};
617618

@@ -628,10 +629,19 @@ std::variant<EOF1Header, EOFValidationError> validate_eof1( // NOLINT(misc-no-r
628629
subcontainer_headers.emplace_back(std::move(subcont_header));
629630
}
630631

631-
for (size_t code_idx = 0; code_idx < header.code_sizes.size(); ++code_idx)
632+
while (!code_sections_worklist.empty())
632633
{
634+
const auto code_idx = code_sections_worklist.front();
635+
code_sections_worklist.pop();
636+
637+
if (visited_code_sections[code_idx])
638+
continue;
639+
640+
visited_code_sections[code_idx] = true;
641+
633642
const auto error_instr = validate_instructions(
634-
rev, header, subcontainer_headers, code_idx, container, accessed_code_sections);
643+
rev, header, subcontainer_headers, code_idx, container, code_sections_worklist);
644+
635645
if (error_instr != EOFValidationError::success)
636646
return error_instr;
637647

@@ -646,7 +656,8 @@ std::variant<EOF1Header, EOFValidationError> validate_eof1( // NOLINT(misc-no-r
646656
return EOFValidationError::invalid_max_stack_height;
647657
}
648658

649-
if (accessed_code_sections.size() != header.code_sizes.size())
659+
if (std::find(visited_code_sections.begin(), visited_code_sections.end(), false) !=
660+
visited_code_sections.end())
650661
return EOFValidationError::unreachable_code_sections;
651662

652663
return header;

test/unittests/eof_validation_test.cpp

+37-7
Original file line numberDiff line numberDiff line change
@@ -691,8 +691,8 @@ TEST_F(eof_validation, max_stack_height)
691691
0x400 * OP_POP + OP_STOP + OP_RETF,
692692
EOFValidationError::max_stack_height_above_limit);
693693

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

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

860860
// Invalid with RETF
861-
add_test_case("EF0001 010008 02000200010001 040000 00 0080000000800000 00 E4",
861+
add_test_case(eof_bytecode(jumpf(1)).code(OP_RETF, 0, 0x80, 0),
862862
EOFValidationError::invalid_non_returning_flag);
863863
add_test_case("EF0001 010004 0200010001 040000 00 00800000 E4",
864864
EOFValidationError::invalid_non_returning_flag);
865865
// Invalid with JUMPF to returning
866866
add_test_case(
867-
"EF0001 01000c 020003000100030001 040000 00 008000000080000000000000 00 E50002 E4",
867+
"EF0001 01000c 020003000300030001 040000 00 008000000080000000000000 E50001 E50002 E4",
868868
EOFValidationError::invalid_non_returning_flag);
869869
// Invalid with JUMPF to non-returning
870-
add_test_case("EF0001 010008 02000200010003 040000 00 0080000000000000 00 E50000",
870+
add_test_case("EF0001 010008 02000200030003 040000 00 0080000000000000 E50001 E50000",
871871
EOFValidationError::invalid_non_returning_flag);
872872
// Invalid with JUMPF to returning and RETF
873873
add_test_case(
874-
"EF0001 01000C 020003000100070001 040000 00 008000000180000100000000 00 E10001E4E50002 E4",
874+
"EF0001 01000C 020003000400070001 040000 00 008000010180000100000000 5FE50001 "
875+
"E10001E4E50002 E4",
875876
EOFValidationError::invalid_non_returning_flag);
876877
// Invalid with JUMPF to non-returning and RETF
877-
add_test_case("EF0001 010008 02000200010007 040000 00 0080000001800001 00 E10001E4E50000",
878+
add_test_case("EF0001 010008 02000200040007 040000 00 0080000101800001 5FE50001 E10001E4E50000",
878879
EOFValidationError::invalid_non_returning_flag);
879880

880881
// Circular JUMPF: can be both returning and non-returning
@@ -971,6 +972,20 @@ TEST_F(eof_validation, unreachable_code_sections)
971972
// Code Section 254 calls itself instead of code section 255, leaving code section 255
972973
// unreachable
973974
add_test_case(code_sections_256_err_254, EOFValidationError::unreachable_code_sections);
975+
976+
// Code Section 0 calls section 1, which calls itself, leaving section
977+
// 2 unreachable
978+
add_test_case(eof_bytecode(jumpf(1)).code(jumpf(1), 0, 0x80, 0).code(jumpf(2), 0, 0x80, 0),
979+
EOFValidationError::unreachable_code_sections);
980+
981+
// Code Section 0 calls section 1, which calls section 2, section 3 and
982+
// 4 call each other but are not reachable from section 0
983+
add_test_case(eof_bytecode(jumpf(1))
984+
.code(jumpf(2), 0, 0x80, 0)
985+
.code(OP_INVALID, 0, 0x80, 0)
986+
.code(jumpf(4), 0, 0x80, 0)
987+
.code(jumpf(3), 0, 0x80, 0),
988+
EOFValidationError::unreachable_code_sections);
974989
}
975990
}
976991

@@ -1166,3 +1181,18 @@ TEST_F(eof_validation, EOF1_unreferenced_subcontainer_valid)
11661181
const auto embedded = eof_bytecode(bytecode{OP_INVALID});
11671182
add_test_case(eof_bytecode(OP_STOP).container(embedded), EOFValidationError::success);
11681183
}
1184+
1185+
TEST_F(eof_validation, EOF1_subcontainer_containing_unreachable_code_sections)
1186+
{
1187+
const auto embedded_1 = eof_bytecode(OP_INVALID).code(OP_INVALID, 0, 0x80, 0);
1188+
add_test_case(eof_bytecode(OP_INVALID).container(embedded_1),
1189+
EOFValidationError::unreachable_code_sections);
1190+
1191+
const auto embedded_2 = eof_bytecode(jumpf(1))
1192+
.code(jumpf(2), 0, 0x80, 0)
1193+
.code(OP_INVALID, 0, 0x80, 0)
1194+
.code(jumpf(4), 0, 0x80, 0)
1195+
.code(jumpf(3), 0, 0x80, 0);
1196+
add_test_case(eof_bytecode(OP_INVALID).container(embedded_2),
1197+
EOFValidationError::unreachable_code_sections);
1198+
}

0 commit comments

Comments
 (0)