diff --git a/lib/evmone/eof.cpp b/lib/evmone/eof.cpp index 57682a7968..4e425d9f81 100644 --- a/lib/evmone/eof.cpp +++ b/lib/evmone/eof.cpp @@ -17,23 +17,26 @@ namespace { constexpr uint8_t MAGIC[] = {0xef, 0x00}; constexpr uint8_t TERMINATOR = 0x00; -constexpr uint8_t CODE_SECTION = 0x01; -constexpr uint8_t DATA_SECTION = 0x02; -constexpr uint8_t TYPE_SECTION = 0x03; -constexpr uint8_t MAX_SECTION = TYPE_SECTION; +constexpr uint8_t TYPE_SECTION = 0x01; +constexpr uint8_t CODE_SECTION = 0x02; +constexpr uint8_t DATA_SECTION = 0x03; +constexpr uint8_t MAX_SECTION = DATA_SECTION; constexpr auto CODE_SECTION_NUMBER_LIMIT = 1024; +constexpr auto MAX_STACK_HEIGHT = 0x0400; using EOFSectionHeaders = std::array, MAX_SECTION + 1>; size_t eof_header_size(const EOFSectionHeaders& headers) noexcept { - const auto section_count = - headers[TYPE_SECTION].size() + headers[CODE_SECTION].size() + headers[DATA_SECTION].size(); + const auto non_code_section_count = headers[TYPE_SECTION].size() + headers[DATA_SECTION].size(); + const auto code_section_count = headers[CODE_SECTION].size(); - constexpr auto section_header_size = 3; // (SECTION_ID + SIZE) per each section + constexpr auto non_code_section_header_size = 3; // (SECTION_ID + SIZE) per each section + constexpr auto code_section_size_size = 2; return sizeof(MAGIC) + 1 + // 1 version byte - section_count * section_header_size + sizeof(TERMINATOR); + non_code_section_count * non_code_section_header_size + sizeof(CODE_SECTION) + 2 + + code_section_count * code_section_size_size + sizeof(TERMINATOR); } std::pair validate_eof_headers(bytes_view container) @@ -47,6 +50,7 @@ std::pair validate_eof_headers(bytes_view auto state = State::section_id; uint8_t section_id = 0; + uint16_t section_num = 0; EOFSectionHeaders section_headers{}; const auto container_end = container.end(); auto it = container.begin() + std::size(MAGIC) + 1; // MAGIC + VERSION @@ -80,12 +84,19 @@ std::pair validate_eof_headers(bytes_view state = State::section_size; break; case CODE_SECTION: + { if (section_headers[CODE_SECTION].size() == CODE_SECTION_NUMBER_LIMIT) return {{}, EOFValidationError::too_many_code_sections}; if (!section_headers[DATA_SECTION].empty()) return {{}, EOFValidationError::data_section_before_code_section}; + const auto section_number_hi = *it++; + if (it == container_end) + return {{}, EOFValidationError::incomplete_section_number}; + const auto section_number_lo = *it++; + section_num = static_cast((section_number_hi << 8) | section_number_lo); state = State::section_size; break; + } default: return {{}, EOFValidationError::unknown_section_id}; } @@ -93,15 +104,19 @@ std::pair validate_eof_headers(bytes_view } case State::section_size: { - const auto size_hi = *it++; - if (it == container_end) - return {{}, EOFValidationError::incomplete_section_size}; - const auto size_lo = *it++; - const auto section_size = static_cast((size_hi << 8) | size_lo); - if (section_size == 0) - return {{}, EOFValidationError::zero_section_size}; + for (size_t i = 0; i < (section_id == CODE_SECTION ? section_num : 1); ++i) + { + const auto size_hi = *it++; + if (it == container_end) + return {{}, EOFValidationError::incomplete_section_size}; + const auto size_lo = *it++; + const auto section_size = static_cast((size_hi << 8) | size_lo); + if (section_size == 0) + return {{}, EOFValidationError::zero_section_size}; + + section_headers[section_id].emplace_back(section_size); + } - section_headers[section_id].emplace_back(section_size); state = State::section_id; break; } @@ -123,33 +138,39 @@ std::pair validate_eof_headers(bytes_view return {{}, EOFValidationError::invalid_section_bodies_size}; if (!section_headers[TYPE_SECTION].empty() && - section_headers[TYPE_SECTION][0] != section_headers[CODE_SECTION].size() * 2) + section_headers[TYPE_SECTION][0] != section_headers[CODE_SECTION].size() * 4) return {{}, EOFValidationError::invalid_type_section_size}; return {section_headers, EOFValidationError::success}; } -std::pair>, EOFValidationError> validate_types( +std::pair, EOFValidationError> validate_types( bytes_view container, size_t header_size, std::vector type_section_sizes) noexcept { assert(!container.empty()); // guaranteed by EOF headers validation assert(type_section_sizes.size() <= 1); // guaranteed by EOF headers validation if (type_section_sizes.empty()) - return {{{0, 0}}, EOFValidationError::success}; + return {{{0, 0, 0}}, EOFValidationError::success}; - std::vector> types; + std::vector types; // guaranteed by EOF headers validation assert(header_size + type_section_sizes[0] < container.size()); - for (auto offset = header_size; offset < header_size + type_section_sizes[0]; offset += 2) - types.emplace_back(container[offset], container[offset + 1]); + for (auto offset = header_size; offset < header_size + type_section_sizes[0]; offset += 4) + { + types.emplace_back(EOF1TypeHeader{container[offset], container[offset + 1], + static_cast(container[offset + 2] << 8 | container[offset + 3])}); + } // check 1st section is (0, 0) - if (types[0].first != 0 || types[0].second != 0) + if (types[0].inputs_num != 0 || types[0].outputs_num != 0) return {{}, EOFValidationError::invalid_first_section_type}; + if (types[0].max_stack_height > MAX_STACK_HEIGHT) + return {{}, EOFValidationError::invalid_max_stack_height}; + return {types, EOFValidationError::success}; } @@ -283,22 +304,43 @@ EOF1Header read_valid_eof1_header(bytes_view container) while (*it != TERMINATOR) { const auto section_id = *it++; - const auto section_size_hi = *it++; - const auto section_size_lo = *it++; - const auto section_size = static_cast((section_size_hi << 8) | section_size_lo); - section_headers[section_id].emplace_back(section_size); + if (section_id == CODE_SECTION) + { + const auto code_section_num_hi = *it++; + const auto code_section_num_lo = *it++; + const auto code_section_num = + static_cast((code_section_num_hi << 8) | code_section_num_lo); + for (uint16_t i = 0; i < code_section_num; ++i) + { + const auto section_size_hi = *it++; + const auto section_size_lo = *it++; + const auto section_size = + static_cast((section_size_hi << 8) | section_size_lo); + section_headers[section_id].emplace_back(section_size); + } + } + else + { + const auto section_size_hi = *it++; + const auto section_size_lo = *it++; + const auto section_size = + static_cast((section_size_hi << 8) | section_size_lo); + section_headers[section_id].emplace_back(section_size); + } } const auto header_size = eof_header_size(section_headers); EOF1Header header; if (section_headers[TYPE_SECTION].empty()) - header.types.emplace_back(0, 0); + header.types.emplace_back(0, 0, 0); else { for (auto type_offset = header_size; - type_offset < header_size + section_headers[TYPE_SECTION][0]; type_offset += 2) - header.types.emplace_back(container[type_offset], container[type_offset + 1]); + type_offset < header_size + section_headers[TYPE_SECTION][0]; type_offset += 4) + + header.types.emplace_back(container[type_offset], container[type_offset + 1], + container[type_offset + 2] << 8 | container[type_offset + 3]); } header.code_sizes = section_headers[CODE_SECTION]; diff --git a/lib/evmone/eof.hpp b/lib/evmone/eof.hpp index 8c8ec5d765..beef7d7d68 100644 --- a/lib/evmone/eof.hpp +++ b/lib/evmone/eof.hpp @@ -14,6 +14,17 @@ namespace evmone { using bytes_view = std::basic_string_view; +struct EOF1TypeHeader +{ + uint8_t inputs_num; + uint8_t outputs_num; + uint16_t max_stack_height; + + EOF1TypeHeader(uint8_t inputs_num_, uint8_t outputs_num_, uint16_t max_stack_height_) + : inputs_num(inputs_num_), outputs_num(outputs_num_), max_stack_height(max_stack_height_) + {} +}; + struct EOF1Header { /// Size of every code section. @@ -22,7 +33,7 @@ struct EOF1Header std::vector code_offsets; uint16_t data_size = 0; - std::vector> types; + std::vector types; /// Returns offset of code section start. [[nodiscard]] EVMC_EXPORT size_t code_begin(size_t index) const noexcept; @@ -45,6 +56,7 @@ enum class EOFValidationError eof_version_unknown, incomplete_section_size, + incomplete_section_number, code_section_missing, multiple_data_sections, unknown_section_id, @@ -61,6 +73,7 @@ enum class EOFValidationError data_section_before_code_section, invalid_type_section_size, invalid_first_section_type, + invalid_max_stack_height, impossible, }; diff --git a/test/unittests/evm_eof_test.cpp b/test/unittests/evm_eof_test.cpp index f9dadcfd99..92429438c6 100644 --- a/test/unittests/evm_eof_test.cpp +++ b/test/unittests/evm_eof_test.cpp @@ -371,8 +371,8 @@ TEST_P(evm, eof_function_example1) rev = EVMC_SHANGHAI; const auto code = - "EF00 01 030004 01000f 010002 00" - "0000 0201" + "EF00 01 010008 020002 000f 0002 00" + "00000005 02010000" "6001 6008 b00001 " + ret_top() + "03b1"; @@ -391,7 +391,7 @@ TEST_P(evm, eof_function_example2) rev = EVMC_SHANGHAI; const auto code = - "ef0001 030006 01003b 010017 01001d 00 000001010101" + "ef0001 01000c 020003 003b 0017 001d 00 00000400 01010400 01010400" "60043560003560e01c63c766526781145d001c63c6c2ea1781145d00065050600080fd50b00002600052602060" "00f350b0000160005260206000f3" "600181115d0004506001b160018103b0000181029050b1"