From 173dab9a4c5966f95f63053a755cf30d8160649c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 4 Feb 2025 10:49:42 +0100 Subject: [PATCH] eof: Detect unused inputs --- lib/evmone/eof.cpp | 8 ++++++++ lib/evmone/eof.hpp | 1 + test/unittests/eof_validation.cpp | 2 ++ test/unittests/eof_validation_stack_test.cpp | 10 +++++----- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/evmone/eof.cpp b/lib/evmone/eof.cpp index 3b97409698..4ecdb85d7f 100644 --- a/lib/evmone/eof.cpp +++ b/lib/evmone/eof.cpp @@ -424,6 +424,7 @@ std::variant validate_max_stack_height( const auto type = header.get_type(container, func_index); std::vector stack_heights(code.size()); stack_heights[0] = {type.inputs, type.inputs}; + bool stack_bottom_reached = false; for (size_t i = 0; i < code.size();) { @@ -500,6 +501,8 @@ std::variant validate_max_stack_height( if (stack_height.min < stack_height_required) return EOFValidationError::stack_underflow; + else if (stack_height.min == stack_height_required) + stack_bottom_reached = true; const StackHeightRange next_stack_height{ stack_height.min + stack_height_change, stack_height.max + stack_height_change}; @@ -574,6 +577,9 @@ std::variant validate_max_stack_height( i = next; } + if (!stack_bottom_reached) + return EOFValidationError::unused_input; + const auto max_stack_height_it = std::ranges::max_element(stack_heights, [](StackHeightRange lhs, StackHeightRange rhs) noexcept { return lhs.max < rhs.max; }); return max_stack_height_it->max; @@ -975,6 +981,8 @@ std::string_view get_error_message(EOFValidationError err) noexcept return "container_size_above_limit"; case EOFValidationError::unreferenced_subcontainer: return "unreferenced_subcontainer"; + case EOFValidationError::unused_input: + return "unused_input"; case EOFValidationError::impossible: return "impossible"; } diff --git a/lib/evmone/eof.hpp b/lib/evmone/eof.hpp index fc68135917..73690e1ad6 100644 --- a/lib/evmone/eof.hpp +++ b/lib/evmone/eof.hpp @@ -188,6 +188,7 @@ enum class EOFValidationError incompatible_container_kind, container_size_above_limit, unreferenced_subcontainer, + unused_input, impossible, }; diff --git a/test/unittests/eof_validation.cpp b/test/unittests/eof_validation.cpp index 83ba5ad50e..3dcbfb79f7 100644 --- a/test/unittests/eof_validation.cpp +++ b/test/unittests/eof_validation.cpp @@ -97,6 +97,8 @@ std::string_view get_tests_error_message(EOFValidationError err) noexcept return "EOF_ContainerSizeAboveLimit"; case EOFValidationError::unreferenced_subcontainer: return "EOF_UnreferencedSubcontainer"; + case EOFValidationError::unused_input: + return "EOFException.UNUSED_INPUT"; case EOFValidationError::impossible: return "impossible"; } diff --git a/test/unittests/eof_validation_stack_test.cpp b/test/unittests/eof_validation_stack_test.cpp index 80f44ff285..b0f15bbc2d 100644 --- a/test/unittests/eof_validation_stack_test.cpp +++ b/test/unittests/eof_validation_stack_test.cpp @@ -1241,11 +1241,11 @@ TEST_F(eof_validation, jumpf_to_nonreturning) // Exactly required inputs on stack at JUMPF add_test_case(eof_bytecode(3 * OP_PUSH0 + jumpf(1), 3).code(OP_STOP, 3, 0x80, 3), - EOFValidationError::success); + EOFValidationError::unused_input); // Extra items on stack at JUMPF add_test_case(eof_bytecode(4 * OP_PUSH0 + jumpf(1), 4).code(OP_STOP, 3, 0x80, 3), - EOFValidationError::success); + EOFValidationError::unused_input); // Not enough inputs on stack at JUMPF add_test_case(eof_bytecode(2 * OP_PUSH0 + jumpf(1), 2).code(OP_STOP, 3, 0x80, 3), @@ -1266,7 +1266,7 @@ TEST_F(eof_validation, jumpf_to_nonreturning_variable_stack) // JUMPF from [1, 3] stack to non-returning function with 1 inputs add_test_case(eof_bytecode(varstack + jumpf(1), 3).code(Opcode{OP_INVALID}, 1, 0x80, 1), - EOFValidationError::success); + EOFValidationError::unused_input); // JUMPF from [1, 3] stack to non-returning function with 0 inputs add_test_case(eof_bytecode(varstack + jumpf(1), 3).code(Opcode{OP_INVALID}, 0, 0x80, 0), @@ -1324,7 +1324,7 @@ TEST_F(eof_validation, jumpf_stack_overflow_variable_stack) TEST_F(eof_validation, jumpf_with_inputs_stack_overflow) { add_test_case(eof_bytecode(1023 * push0() + jumpf(1), 1023).code(push0() + OP_STOP, 2, 0x80, 3), - EOFValidationError::success); + EOFValidationError::unused_input); add_test_case( eof_bytecode(1023 * push0() + jumpf(1), 1023).code(push0() + push0() + OP_STOP, 2, 0x80, 4), @@ -1340,7 +1340,7 @@ TEST_F(eof_validation, jumpf_with_inputs_stack_overflow_variable_stack) // JUMPF from [1021, 1023] stack to 2 inputs and 3 max stack add_test_case(eof_bytecode(varstack + 1020 * push0() + jumpf(1), 1023) .code(push0() + OP_STOP, 2, 0x80, 3), - EOFValidationError::success); + EOFValidationError::unused_input); // JUMPF from [1021, 1023] stack to 2 inputs and 6 max stack - min and max stack overflow add_test_case(eof_bytecode(varstack + 1020 * push0() + jumpf(1), 1023)