Skip to content

Commit 04a0259

Browse files
committed
Disallow code sections unreachable from section 0
1 parent 24a3214 commit 04a0259

File tree

2 files changed

+48
-16
lines changed

2 files changed

+48
-16
lines changed

lib/evmone/eof.cpp

+25-9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <limits>
1414
#include <numeric>
1515
#include <ostream>
16+
#include <queue>
1617
#include <span>
1718
#include <stack>
1819
#include <unordered_set>
@@ -215,8 +216,7 @@ std::variant<std::vector<EOFCodeType>, EOFValidationError> validate_types(
215216
}
216217

217218
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
219+
size_t code_idx, bytes_view container, std::queue<uint16_t>& code_sections_worklist) noexcept
220220
{
221221
const bytes_view code{header.get_code(container, code_idx)};
222222
assert(!code.empty()); // guaranteed by EOF headers validation
@@ -249,7 +249,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
249249
if (header.types[fid].outputs == NON_RETURNING_FUNCTION)
250250
return EOFValidationError::callf_to_non_returning_function;
251251
if (code_idx != fid)
252-
accessed_code_sections.insert(fid);
252+
code_sections_worklist.push(fid);
253253
i += 2;
254254
}
255255
else if (op == OP_RETF)
@@ -266,7 +266,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
266266
if (header.types[fid].outputs != NON_RETURNING_FUNCTION)
267267
is_returning = true;
268268
if (code_idx != fid)
269-
accessed_code_sections.insert(fid);
269+
code_sections_worklist.push(fid);
270270
i += 2;
271271
}
272272
else if (op == OP_DATALOADN)
@@ -551,13 +551,25 @@ std::variant<EOF1Header, EOFValidationError> validate_eof1(
551551
offset += code_size;
552552
}
553553

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

557-
for (size_t code_idx = 0; code_idx < header.code_sizes.size(); ++code_idx)
560+
while (!code_sections_worklist.empty())
558561
{
562+
auto code_idx = code_sections_worklist.front();
563+
code_sections_worklist.pop();
564+
565+
if (visited_code_sections[code_idx])
566+
continue;
567+
568+
visited_code_sections[code_idx] = true;
569+
559570
const auto error_instr =
560-
validate_instructions(rev, header, code_idx, container, accessed_code_sections);
571+
validate_instructions(rev, header, code_idx, container, code_sections_worklist);
572+
561573
if (error_instr != EOFValidationError::success)
562574
return error_instr;
563575

@@ -566,14 +578,18 @@ std::variant<EOF1Header, EOFValidationError> validate_eof1(
566578

567579
auto msh_or_error =
568580
validate_max_stack_height(header.get_code(container, code_idx), code_idx, header.types);
581+
569582
if (const auto* error = std::get_if<EOFValidationError>(&msh_or_error))
570583
return *error;
571584
if (std::get<int32_t>(msh_or_error) != header.types[code_idx].max_stack_height)
572585
return EOFValidationError::invalid_max_stack_height;
573586
}
574587

575-
if (accessed_code_sections.size() != header.code_sizes.size())
576-
return EOFValidationError::unreachable_code_sections;
588+
for (auto&& visited_code_section : visited_code_sections)
589+
{
590+
if (!visited_code_section)
591+
return EOFValidationError::unreachable_code_sections;
592+
}
577593

578594
return header;
579595
}

test/unittests/eof_validation_test.cpp

+23-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(bytecode{"EF0001 010008 02000200040C01 040000 00 00800000 000003FF"} + OP_CALLF +
633+
"0001" + OP_STOP + 0x400 * bytecode{1} + 0x400 * OP_POP + OP_RETF,
634634
EOFValidationError::invalid_max_stack_height);
635635

636636
add_test_case("EF0001 010008 0200020C010001 040000 00 008003FF 00000000" + 0x400 * bytecode{1} +
@@ -796,23 +796,25 @@ 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);
801+
801802
add_test_case("EF0001 010004 0200010001 040000 00 00800000 E4",
802803
EOFValidationError::invalid_non_returning_flag);
803804
// Invalid with JUMPF to returning
804805
add_test_case(
805-
"EF0001 01000c 020003000100030001 040000 00 008000000080000000000000 00 E50002 E4",
806+
"EF0001 01000c 020003000300030001 040000 00 008000000080000000000000 E50001 E50002 E4",
806807
EOFValidationError::invalid_non_returning_flag);
807808
// Invalid with JUMPF to non-returning
808-
add_test_case("EF0001 010008 02000200010003 040000 00 0080000000000000 00 E50000",
809+
add_test_case("EF0001 010008 02000200030003 040000 00 0080000000000000 E50001 E50000",
809810
EOFValidationError::invalid_non_returning_flag);
810811
// Invalid with JUMPF to returning and RETF
811812
add_test_case(
812-
"EF0001 01000C 020003000100070001 040000 00 008000000180000100000000 00 E10001E4E50002 E4",
813+
"EF0001 01000C 020003000400070001 040000 00 008000010180000100000000 5FE50001 "
814+
"E10001E4E50002 E4",
813815
EOFValidationError::invalid_non_returning_flag);
814816
// Invalid with JUMPF to non-returning and RETF
815-
add_test_case("EF0001 010008 02000200010007 040000 00 0080000001800001 00 E10001E4E50000",
817+
add_test_case("EF0001 010008 02000200040007 040000 00 0080000101800001 5FE50001 E10001E4E50000",
816818
EOFValidationError::invalid_non_returning_flag);
817819

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

0 commit comments

Comments
 (0)