Skip to content

Commit 8b5e65a

Browse files
committed
Disallow code sections unreachable from section 0
1 parent 24a3214 commit 8b5e65a

File tree

2 files changed

+42
-16
lines changed

2 files changed

+42
-16
lines changed

lib/evmone/eof.cpp

+20-9
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
#include <limits>
1414
#include <numeric>
1515
#include <ostream>
16+
#include <queue>
1617
#include <span>
1718
#include <stack>
18-
#include <unordered_set>
1919
#include <variant>
2020
#include <vector>
2121

@@ -215,8 +215,7 @@ std::variant<std::vector<EOFCodeType>, EOFValidationError> validate_types(
215215
}
216216

217217
EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& header,
218-
size_t code_idx, bytes_view container,
219-
std::unordered_set<uint16_t>& accessed_code_sections) noexcept
218+
size_t code_idx, bytes_view container, std::queue<uint16_t>& code_sections_worklist) noexcept
220219
{
221220
const bytes_view code{header.get_code(container, code_idx)};
222221
assert(!code.empty()); // guaranteed by EOF headers validation
@@ -249,7 +248,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
249248
if (header.types[fid].outputs == NON_RETURNING_FUNCTION)
250249
return EOFValidationError::callf_to_non_returning_function;
251250
if (code_idx != fid)
252-
accessed_code_sections.insert(fid);
251+
code_sections_worklist.push(fid);
253252
i += 2;
254253
}
255254
else if (op == OP_RETF)
@@ -266,7 +265,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
266265
if (header.types[fid].outputs != NON_RETURNING_FUNCTION)
267266
is_returning = true;
268267
if (code_idx != fid)
269-
accessed_code_sections.insert(fid);
268+
code_sections_worklist.push(fid);
270269
i += 2;
271270
}
272271
else if (op == OP_DATALOADN)
@@ -551,13 +550,24 @@ std::variant<EOF1Header, EOFValidationError> validate_eof1(
551550
offset += code_size;
552551
}
553552

554-
std::unordered_set<uint16_t> accessed_code_sections = {0};
553+
std::vector<bool> visited_code_sections(code_sizes.size());
554+
std::queue<uint16_t> code_sections_worklist({0});
555+
555556
EOF1Header header{container[2], code_sizes, code_offsets, data_size, types};
556557

557-
for (size_t code_idx = 0; code_idx < header.code_sizes.size(); ++code_idx)
558+
while (!code_sections_worklist.empty())
558559
{
560+
auto code_idx = code_sections_worklist.front();
561+
code_sections_worklist.pop();
562+
563+
if (visited_code_sections[code_idx])
564+
continue;
565+
566+
visited_code_sections[code_idx] = true;
567+
559568
const auto error_instr =
560-
validate_instructions(rev, header, code_idx, container, accessed_code_sections);
569+
validate_instructions(rev, header, code_idx, container, code_sections_worklist);
570+
561571
if (error_instr != EOFValidationError::success)
562572
return error_instr;
563573

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

575-
if (accessed_code_sections.size() != header.code_sizes.size())
585+
if (std::find(visited_code_sections.begin(), visited_code_sections.end(), false) !=
586+
visited_code_sections.end())
576587
return EOFValidationError::unreachable_code_sections;
577588

578589
return header;

test/unittests/eof_validation_test.cpp

+22-7
Original file line numberDiff line numberDiff line change
@@ -629,8 +629,8 @@ TEST_F(eof_validation, max_stack_height)
629629
0x400 * OP_POP + OP_STOP + OP_RETF,
630630
EOFValidationError::max_stack_height_above_limit);
631631

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

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

798798
// Invalid with RETF
799-
add_test_case("EF0001 010008 02000200010001 040000 00 0080000000800000 00 E4",
799+
add_test_case(eof_bytecode(jumpf(1)).code(OP_RETF, 0, 0x80, 0),
800800
EOFValidationError::invalid_non_returning_flag);
801801
add_test_case("EF0001 010004 0200010001 040000 00 00800000 E4",
802802
EOFValidationError::invalid_non_returning_flag);
803803
// Invalid with JUMPF to returning
804804
add_test_case(
805-
"EF0001 01000c 020003000100030001 040000 00 008000000080000000000000 00 E50002 E4",
805+
"EF0001 01000c 020003000300030001 040000 00 008000000080000000000000 E50001 E50002 E4",
806806
EOFValidationError::invalid_non_returning_flag);
807807
// Invalid with JUMPF to non-returning
808-
add_test_case("EF0001 010008 02000200010003 040000 00 0080000000000000 00 E50000",
808+
add_test_case("EF0001 010008 02000200030003 040000 00 0080000000000000 E50001 E50000",
809809
EOFValidationError::invalid_non_returning_flag);
810810
// Invalid with JUMPF to returning and RETF
811811
add_test_case(
812-
"EF0001 01000C 020003000100070001 040000 00 008000000180000100000000 00 E10001E4E50002 E4",
812+
"EF0001 01000C 020003000400070001 040000 00 008000010180000100000000 5FE50001 "
813+
"E10001E4E50002 E4",
813814
EOFValidationError::invalid_non_returning_flag);
814815
// Invalid with JUMPF to non-returning and RETF
815-
add_test_case("EF0001 010008 02000200010007 040000 00 0080000001800001 00 E10001E4E50000",
816+
add_test_case("EF0001 010008 02000200040007 040000 00 0080000101800001 5FE50001 E10001E4E50000",
816817
EOFValidationError::invalid_non_returning_flag);
817818

818819
// Circular JUMPF: can be both returning and non-returning
@@ -909,5 +910,19 @@ TEST_F(eof_validation, unreachable_code_sections)
909910
// Code Section 254 calls itself instead of code section 255, leaving code section 255
910911
// unreachable
911912
add_test_case(code_sections_256_err_254, EOFValidationError::unreachable_code_sections);
913+
914+
// Code Section 0 calls section 1, which calls itself, leaving section
915+
// 2 unreachable
916+
add_test_case(eof_bytecode(jumpf(1)).code(jumpf(1), 0, 0x80, 0).code(jumpf(2), 0, 0x80, 0),
917+
EOFValidationError::unreachable_code_sections);
918+
919+
// Code Section 0 calls section 1, which calls section 2, section 3 and
920+
// 4 call each other but are not reachable from section 0
921+
add_test_case(eof_bytecode(jumpf(1))
922+
.code(jumpf(2), 0, 0x80, 0)
923+
.code(OP_INVALID, 0, 0x80, 0)
924+
.code(jumpf(4), 0, 0x80, 0)
925+
.code(jumpf(3), 0, 0x80, 0),
926+
EOFValidationError::unreachable_code_sections);
912927
}
913928
}

0 commit comments

Comments
 (0)