Skip to content

Commit 903dae2

Browse files
committed
Switch to splitting eof_validation in 2 phases
1 parent 22794b7 commit 903dae2

File tree

6 files changed

+177
-181
lines changed

6 files changed

+177
-181
lines changed

lib/evmone/eof.cpp

+138-150
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,19 @@
99

1010
#include <intx/intx.hpp>
1111
#include <algorithm>
12-
#include <array>
1312
#include <cassert>
1413
#include <limits>
1514
#include <numeric>
1615
#include <ostream>
1716
#include <queue>
1817
#include <span>
1918
#include <stack>
20-
#include <variant>
2119
#include <vector>
2220

2321
namespace evmone
2422
{
2523
namespace
2624
{
27-
constexpr uint8_t MAGIC[] = {0xef, 0x00};
28-
constexpr uint8_t TERMINATOR = 0x00;
29-
constexpr uint8_t TYPE_SECTION = 0x01;
30-
constexpr uint8_t CODE_SECTION = 0x02;
31-
constexpr uint8_t CONTAINER_SECTION = 0x03;
32-
constexpr uint8_t DATA_SECTION = 0x04;
33-
constexpr uint8_t MAX_SECTION = DATA_SECTION;
3425
constexpr auto CODE_SECTION_NUMBER_LIMIT = 1024;
3526
constexpr auto CONTAINER_SECTION_NUMBER_LIMIT = 256;
3627
constexpr auto MAX_STACK_HEIGHT = 0x03FF;
@@ -39,8 +30,6 @@ constexpr auto REL_OFFSET_SIZE = sizeof(int16_t);
3930
constexpr auto STACK_SIZE_LIMIT = 1024;
4031
constexpr uint8_t NON_RETURNING_FUNCTION = 0x80;
4132

42-
using EOFSectionHeaders = std::array<std::vector<uint16_t>, MAX_SECTION + 1>;
43-
4433
size_t eof_header_size(const EOFSectionHeaders& headers) noexcept
4534
{
4635
const auto non_code_section_count = 2; // type section and data section
@@ -79,140 +68,6 @@ EOFValidationError get_section_missing_error(uint8_t section_id) noexcept
7968
}
8069
}
8170

82-
std::variant<EOFSectionHeaders, EOFValidationError> validate_section_headers(bytes_view container)
83-
{
84-
enum class State
85-
{
86-
section_id,
87-
section_size,
88-
terminated
89-
};
90-
91-
auto state = State::section_id;
92-
uint8_t section_id = 0;
93-
uint16_t section_num = 0;
94-
EOFSectionHeaders section_headers{};
95-
const auto container_end = container.end();
96-
auto it = container.begin() + std::size(MAGIC) + 1; // MAGIC + VERSION
97-
uint8_t expected_section_id = TYPE_SECTION;
98-
while (it != container_end && state != State::terminated)
99-
{
100-
switch (state)
101-
{
102-
case State::section_id:
103-
{
104-
section_id = *it++;
105-
106-
// If DATA_SECTION is expected, CONTAINER_SECTION is also allowed, because
107-
// container section is optional.
108-
if (section_id != expected_section_id &&
109-
(expected_section_id != DATA_SECTION || section_id != CONTAINER_SECTION))
110-
return get_section_missing_error(expected_section_id);
111-
112-
switch (section_id)
113-
{
114-
case TERMINATOR:
115-
state = State::terminated;
116-
break;
117-
case TYPE_SECTION:
118-
expected_section_id = CODE_SECTION;
119-
state = State::section_size;
120-
break;
121-
case CODE_SECTION:
122-
{
123-
if (it >= container_end - 1)
124-
return EOFValidationError::incomplete_section_number;
125-
section_num = read_uint16_be(it);
126-
it += 2;
127-
if (section_num == 0)
128-
return EOFValidationError::zero_section_size;
129-
if (section_num > CODE_SECTION_NUMBER_LIMIT)
130-
return EOFValidationError::too_many_code_sections;
131-
expected_section_id = DATA_SECTION;
132-
state = State::section_size;
133-
break;
134-
}
135-
case DATA_SECTION:
136-
expected_section_id = TERMINATOR;
137-
state = State::section_size;
138-
break;
139-
case CONTAINER_SECTION:
140-
{
141-
if (it >= container_end - 1)
142-
return EOFValidationError::incomplete_section_number;
143-
section_num = read_uint16_be(it);
144-
it += 2;
145-
if (section_num == 0)
146-
return EOFValidationError::zero_section_size;
147-
if (section_num > CONTAINER_SECTION_NUMBER_LIMIT)
148-
return EOFValidationError::too_many_container_sections;
149-
expected_section_id = DATA_SECTION;
150-
state = State::section_size;
151-
break;
152-
}
153-
default:
154-
assert(false);
155-
}
156-
break;
157-
}
158-
case State::section_size:
159-
{
160-
if (section_id == CODE_SECTION || section_id == CONTAINER_SECTION)
161-
{
162-
assert(section_num > 0); // Guaranteed by previous validation step.
163-
for (size_t i = 0; i < section_num; ++i)
164-
{
165-
if (it >= container_end - 1)
166-
return EOFValidationError::incomplete_section_size;
167-
const auto section_size = read_uint16_be(it);
168-
it += 2;
169-
if (section_size == 0)
170-
return EOFValidationError::zero_section_size;
171-
172-
section_headers[section_id].emplace_back(section_size);
173-
}
174-
}
175-
else // TYPES_SECTION or DATA_SECTION
176-
{
177-
if (it >= container_end - 1)
178-
return EOFValidationError::incomplete_section_size;
179-
const auto section_size = read_uint16_be(it);
180-
it += 2;
181-
if (section_size == 0 && section_id != DATA_SECTION)
182-
return EOFValidationError::zero_section_size;
183-
184-
section_headers[section_id].emplace_back(section_size);
185-
}
186-
187-
state = State::section_id;
188-
break;
189-
}
190-
case State::terminated:
191-
return EOFValidationError::impossible;
192-
}
193-
}
194-
195-
if (state != State::terminated)
196-
return EOFValidationError::section_headers_not_terminated;
197-
198-
const auto section_bodies_without_data =
199-
section_headers[TYPE_SECTION].front() +
200-
std::accumulate(
201-
section_headers[CODE_SECTION].begin(), section_headers[CODE_SECTION].end(), 0) +
202-
std::accumulate(section_headers[CONTAINER_SECTION].begin(),
203-
section_headers[CONTAINER_SECTION].end(), 0);
204-
const auto remaining_container_size = container_end - it;
205-
// Only data section may be truncated, so remaining_container size must be at least
206-
// declared_size_without_data
207-
if (remaining_container_size < section_bodies_without_data)
208-
return EOFValidationError::invalid_section_bodies_size;
209-
210-
if (section_headers[TYPE_SECTION][0] != section_headers[CODE_SECTION].size() * 4)
211-
return EOFValidationError::invalid_type_section_size;
212-
213-
return section_headers;
214-
}
215-
21671
std::variant<std::vector<EOFCodeType>, EOFValidationError> validate_types(
21772
bytes_view container, size_t header_size, uint16_t type_section_size) noexcept
21873
{
@@ -294,6 +149,9 @@ std::variant<EOF1Header, EOFValidationError> validate_header(
294149
}
295150
const auto data_offset = static_cast<uint16_t>(offset);
296151

152+
if (container.size() > offset + data_size)
153+
return EOFValidationError::invalid_section_bodies_size;
154+
297155
return EOF1Header{
298156
.version = container[2],
299157
.code_sizes = code_sizes,
@@ -656,9 +514,6 @@ EOFValidationError validate_eof1(evmc_revision rev, bytes_view main_container) n
656514

657515
auto& subcont_header = std::get<EOF1Header>(error_subcont_or_header);
658516

659-
if (subcont_header.has_stray_bytes(subcontainer.size()))
660-
return EOFValidationError::stray_bytes_subcontainer;
661-
662517
subcontainer_data_offsets.push_back(subcont_header.data_offset);
663518
subcontainer_data_sizes.push_back(subcont_header.data_size);
664519

@@ -728,6 +583,141 @@ bool is_eof_container(bytes_view container) noexcept
728583
return container.size() > 1 && container[0] == MAGIC[0] && container[1] == MAGIC[1];
729584
}
730585

586+
std::variant<EOFSectionHeaders, EOFValidationError> validate_section_headers(
587+
bytes_view container) noexcept
588+
{
589+
enum class State
590+
{
591+
section_id,
592+
section_size,
593+
terminated
594+
};
595+
596+
auto state = State::section_id;
597+
uint8_t section_id = 0;
598+
uint16_t section_num = 0;
599+
EOFSectionHeaders section_headers{};
600+
const auto container_end = container.end();
601+
auto it = container.begin() + std::size(MAGIC) + 1; // MAGIC + VERSION
602+
uint8_t expected_section_id = TYPE_SECTION;
603+
while (it != container_end && state != State::terminated)
604+
{
605+
switch (state)
606+
{
607+
case State::section_id:
608+
{
609+
section_id = *it++;
610+
611+
// If DATA_SECTION is expected, CONTAINER_SECTION is also allowed, because
612+
// container section is optional.
613+
if (section_id != expected_section_id &&
614+
(expected_section_id != DATA_SECTION || section_id != CONTAINER_SECTION))
615+
return get_section_missing_error(expected_section_id);
616+
617+
switch (section_id)
618+
{
619+
case TERMINATOR:
620+
state = State::terminated;
621+
break;
622+
case TYPE_SECTION:
623+
expected_section_id = CODE_SECTION;
624+
state = State::section_size;
625+
break;
626+
case CODE_SECTION:
627+
{
628+
if (it >= container_end - 1)
629+
return EOFValidationError::incomplete_section_number;
630+
section_num = read_uint16_be(it);
631+
it += 2;
632+
if (section_num == 0)
633+
return EOFValidationError::zero_section_size;
634+
if (section_num > CODE_SECTION_NUMBER_LIMIT)
635+
return EOFValidationError::too_many_code_sections;
636+
expected_section_id = DATA_SECTION;
637+
state = State::section_size;
638+
break;
639+
}
640+
case DATA_SECTION:
641+
expected_section_id = TERMINATOR;
642+
state = State::section_size;
643+
break;
644+
case CONTAINER_SECTION:
645+
{
646+
if (it >= container_end - 1)
647+
return EOFValidationError::incomplete_section_number;
648+
section_num = read_uint16_be(it);
649+
it += 2;
650+
if (section_num == 0)
651+
return EOFValidationError::zero_section_size;
652+
if (section_num > CONTAINER_SECTION_NUMBER_LIMIT)
653+
return EOFValidationError::too_many_container_sections;
654+
expected_section_id = DATA_SECTION;
655+
state = State::section_size;
656+
break;
657+
}
658+
default:
659+
assert(false);
660+
}
661+
break;
662+
}
663+
case State::section_size:
664+
{
665+
if (section_id == CODE_SECTION || section_id == CONTAINER_SECTION)
666+
{
667+
assert(section_num > 0); // Guaranteed by previous validation step.
668+
for (size_t i = 0; i < section_num; ++i)
669+
{
670+
if (it >= container_end - 1)
671+
return EOFValidationError::incomplete_section_size;
672+
const auto section_size = read_uint16_be(it);
673+
it += 2;
674+
if (section_size == 0)
675+
return EOFValidationError::zero_section_size;
676+
677+
section_headers[section_id].emplace_back(section_size);
678+
}
679+
}
680+
else // TYPES_SECTION or DATA_SECTION
681+
{
682+
if (it >= container_end - 1)
683+
return EOFValidationError::incomplete_section_size;
684+
const auto section_size = read_uint16_be(it);
685+
it += 2;
686+
if (section_size == 0 && section_id != DATA_SECTION)
687+
return EOFValidationError::zero_section_size;
688+
689+
section_headers[section_id].emplace_back(section_size);
690+
}
691+
692+
state = State::section_id;
693+
break;
694+
}
695+
case State::terminated:
696+
return EOFValidationError::impossible;
697+
}
698+
}
699+
700+
if (state != State::terminated)
701+
return EOFValidationError::section_headers_not_terminated;
702+
703+
const auto section_bodies_without_data =
704+
section_headers[TYPE_SECTION].front() +
705+
std::accumulate(
706+
section_headers[CODE_SECTION].begin(), section_headers[CODE_SECTION].end(), 0) +
707+
std::accumulate(section_headers[CONTAINER_SECTION].begin(),
708+
section_headers[CONTAINER_SECTION].end(), 0);
709+
const auto remaining_container_size = container_end - it;
710+
// Only data section may be truncated, so remaining_container size must be at least
711+
// declared_size_without_data
712+
if (remaining_container_size < section_bodies_without_data)
713+
return EOFValidationError::invalid_section_bodies_size;
714+
715+
if (section_headers[TYPE_SECTION][0] != section_headers[CODE_SECTION].size() * 4)
716+
return EOFValidationError::invalid_type_section_size;
717+
718+
return section_headers;
719+
}
720+
731721
/// This function expects the prefix and version to be valid, as it ignores it.
732722
EOF1Header read_valid_eof1_header(bytes_view container)
733723
{
@@ -905,8 +895,6 @@ std::string_view get_error_message(EOFValidationError err) noexcept
905895
return "invalid_container_section_index";
906896
case EOFValidationError::eofcreate_with_truncated_container:
907897
return "eofcreate_with_truncated_container";
908-
case EOFValidationError::stray_bytes_subcontainer:
909-
return "stray_bytes_subcontainer";
910898
case EOFValidationError::impossible:
911899
return "impossible";
912900
}

0 commit comments

Comments
 (0)