17
17
#include < queue>
18
18
#include < span>
19
19
#include < stack>
20
+ #include < unordered_set>
20
21
#include < variant>
21
22
#include < vector>
22
23
@@ -309,17 +310,27 @@ std::variant<EOF1Header, EOFValidationError> validate_header(
309
310
};
310
311
}
311
312
312
- EOFValidationError validate_instructions (evmc_revision rev, const EOF1Header& header,
313
- std::span<const uint16_t > subcontainer_data_offsets,
314
- std::span<const uint16_t > subcontainer_data_sizes, size_t code_idx, bytes_view container,
315
- std::queue<uint16_t >& code_sections_worklist) noexcept
313
+ // / Result of validating instructions in a code section.
314
+ struct InstructionValidationResult
315
+ {
316
+ // / Pairs of (container_index, opcode) of all opcodes referencing subcontainers in this section.
317
+ std::vector<std::pair<uint8_t , Opcode>> subcontainer_references;
318
+ // / Set of accessed code section indices.
319
+ // TODO: Vector can be used here in case unordered_set causes performance issues.
320
+ std::unordered_set<uint16_t > accessed_code_sections;
321
+ };
322
+
323
+ std::variant<InstructionValidationResult, EOFValidationError> validate_instructions (
324
+ evmc_revision rev, const EOF1Header& header, size_t code_idx, bytes_view container) noexcept
316
325
{
317
326
const bytes_view code{header.get_code (container, code_idx)};
318
327
assert (!code.empty ()); // guaranteed by EOF headers validation
319
328
320
329
const auto & cost_table = baseline::get_baseline_cost_table (rev, 1 );
321
330
322
331
bool is_returning = false ;
332
+ std::unordered_set<uint16_t > accessed_code_sections;
333
+ std::vector<std::pair<uint8_t , Opcode>> subcontainer_references;
323
334
324
335
for (size_t i = 0 ; i < code.size (); ++i)
325
336
{
@@ -345,7 +356,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
345
356
if (header.types [fid].outputs == NON_RETURNING_FUNCTION)
346
357
return EOFValidationError::callf_to_non_returning_function;
347
358
if (code_idx != fid)
348
- code_sections_worklist. push (fid);
359
+ accessed_code_sections. insert (fid);
349
360
i += 2 ;
350
361
}
351
362
else if (op == OP_RETF)
@@ -362,7 +373,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
362
373
if (header.types [fid].outputs != NON_RETURNING_FUNCTION)
363
374
is_returning = true ;
364
375
if (code_idx != fid)
365
- code_sections_worklist. push (fid);
376
+ accessed_code_sections. insert (fid);
366
377
i += 2 ;
367
378
}
368
379
else if (op == OP_DATALOADN)
@@ -378,11 +389,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
378
389
if (container_idx >= header.container_sizes .size ())
379
390
return EOFValidationError::invalid_container_section_index;
380
391
381
- const auto subcontainer_end =
382
- subcontainer_data_offsets[container_idx] + subcontainer_data_sizes[container_idx];
383
- if (op == OP_EOFCREATE && subcontainer_end != header.container_sizes [container_idx])
384
- return EOFValidationError::eofcreate_with_truncated_container;
385
-
392
+ subcontainer_references.emplace_back (container_idx, Opcode{op});
386
393
++i;
387
394
}
388
395
else
@@ -393,7 +400,7 @@ EOFValidationError validate_instructions(evmc_revision rev, const EOF1Header& he
393
400
if (is_returning != declared_returning)
394
401
return EOFValidationError::invalid_non_returning_flag;
395
402
396
- return EOFValidationError::success ;
403
+ return InstructionValidationResult{subcontainer_references, accessed_code_sections} ;
397
404
}
398
405
399
406
// / Validates that that we don't rjump inside an instruction's immediate.
@@ -633,42 +640,33 @@ std::variant<EOFValidationError, int32_t> validate_max_stack_height(
633
640
634
641
EOFValidationError validate_eof1 (evmc_revision rev, bytes_view main_container) noexcept
635
642
{
636
- const auto error_or_header = validate_header (rev, main_container);
637
- if ( const auto * error = std::get_if<EOFValidationError>(&error_or_header))
638
- return *error ;
639
- const auto & main_container_header = std::get<EOF1Header>(error_or_header) ;
640
-
641
- // Queue of (container, header) pairs left to process
642
- std::queue<std::pair<bytes_view, EOF1Header> > container_queue;
643
- container_queue.emplace ( main_container, main_container_header );
643
+ struct ContainerValidation
644
+ {
645
+ bytes_view bytes ;
646
+ bool referenced_by_eofcreate = false ;
647
+ };
648
+ // Queue of containers left to process
649
+ std::queue<ContainerValidation > container_queue;
650
+ container_queue.push ({ main_container, false } );
644
651
645
652
while (!container_queue.empty ())
646
653
{
647
- // Validate subcontainer headers and get their data sections
648
- const auto & [container, header] = container_queue.front ();
649
- std::vector<uint16_t > subcontainer_data_offsets;
650
- std::vector<uint16_t > subcontainer_data_sizes;
651
- for (size_t subcont_idx = 0 ; subcont_idx < header.container_sizes .size (); ++subcont_idx)
652
- {
653
- const bytes_view subcontainer{header.get_container (container, subcont_idx)};
654
+ const auto & [container, referenced_by_eofcreate] = container_queue.front ();
654
655
655
- auto error_subcont_or_header = validate_header (rev, subcontainer);
656
- if ( const auto * error_subcont =
657
- std::get_if<EOFValidationError>(&error_subcont_or_header ))
658
- return *error_subcont ;
656
+ // Validate header
657
+ auto error_or_header = validate_header (rev, container);
658
+ if ( const auto * error = std::get_if<EOFValidationError>(&error_or_header ))
659
+ return *error ;
659
660
660
- auto & subcont_header = std::get<EOF1Header>(error_subcont_or_header);
661
-
662
- subcontainer_data_offsets.push_back (subcont_header.data_offset );
663
- subcontainer_data_sizes.push_back (subcont_header.data_size );
664
-
665
- container_queue.emplace (subcontainer, std::move (subcont_header));
666
- }
661
+ auto & header = std::get<EOF1Header>(error_or_header);
667
662
668
663
// Validate code sections
669
664
std::vector<bool > visited_code_sections (header.code_sizes .size ());
670
665
std::queue<uint16_t > code_sections_queue ({0 });
671
666
667
+ const auto subcontainer_count = header.container_sizes .size ();
668
+ std::vector<bool > subcontainer_referenced_by_eofcreate (subcontainer_count, false );
669
+
672
670
while (!code_sections_queue.empty ())
673
671
{
674
672
const auto code_idx = code_sections_queue.front ();
@@ -680,11 +678,25 @@ EOFValidationError validate_eof1(evmc_revision rev, bytes_view main_container) n
680
678
visited_code_sections[code_idx] = true ;
681
679
682
680
// Validate instructions
683
- const auto error_instr = validate_instructions (rev, header, subcontainer_data_offsets,
684
- subcontainer_data_sizes, code_idx, container, code_sections_queue);
681
+ const auto instr_validation_result_or_error =
682
+ validate_instructions (rev, header, code_idx, container);
683
+ if (const auto * error =
684
+ std::get_if<EOFValidationError>(&instr_validation_result_or_error))
685
+ return *error;
686
+
687
+ const auto & [subcontainer_references, accessed_code_sections] =
688
+ std::get<InstructionValidationResult>(instr_validation_result_or_error);
689
+
690
+ // Mark what instructions referenced which subcontainers.
691
+ for (const auto & [index , opcode] : subcontainer_references)
692
+ {
693
+ if (opcode == OP_EOFCREATE)
694
+ subcontainer_referenced_by_eofcreate[index ] = true ;
695
+ }
685
696
686
- if (error_instr != EOFValidationError::success)
687
- return error_instr;
697
+ // TODO(C++23): can use push_range()
698
+ for (const auto section_id : accessed_code_sections)
699
+ code_sections_queue.push (section_id);
688
700
689
701
// Validate jump destinations
690
702
if (!validate_rjump_destinations (header.get_code (container, code_idx)))
@@ -703,6 +715,17 @@ EOFValidationError validate_eof1(evmc_revision rev, bytes_view main_container) n
703
715
visited_code_sections.end ())
704
716
return EOFValidationError::unreachable_code_sections;
705
717
718
+ if (referenced_by_eofcreate && !header.can_init (container.size ()))
719
+ return EOFValidationError::eofcreate_with_truncated_container;
720
+
721
+ // Enqueue subcontainers
722
+ for (size_t subcont_idx = 0 ; subcont_idx < subcontainer_count; ++subcont_idx)
723
+ {
724
+ const bytes_view subcontainer{header.get_container (container, subcont_idx)};
725
+
726
+ container_queue.push ({subcontainer, subcontainer_referenced_by_eofcreate[subcont_idx]});
727
+ }
728
+
706
729
container_queue.pop ();
707
730
}
708
731
0 commit comments