diff --git a/libraries/chain/webassembly/compiler_builtins.cpp b/libraries/chain/webassembly/compiler_builtins.cpp index 01f1180949..2ccda54da4 100644 --- a/libraries/chain/webassembly/compiler_builtins.cpp +++ b/libraries/chain/webassembly/compiler_builtins.cpp @@ -45,6 +45,12 @@ namespace eosio { namespace chain { namespace webassembly { EOS_ASSERT(rhs != 0, arithmetic_exception, "divide by zero"); + //force integer overflow to return dividend unchanged + if(lhs == std::numeric_limits<__int128>::min() && rhs == -1) { + *ret = lhs; + return; + } + lhs /= rhs; *ret = lhs; @@ -92,6 +98,12 @@ namespace eosio { namespace chain { namespace webassembly { EOS_ASSERT(rhs != 0, arithmetic_exception, "divide by zero"); + //force undefined behavior (due to lhs/rhs being an overflow) to return zero + if(lhs == std::numeric_limits<__int128>::min() && rhs == -1) { + *ret = 0; + return; + } + lhs %= rhs; *ret = lhs; } diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index ede627637b..59bdb6568e 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -39,6 +39,7 @@ #include #include +#include #include "test_cfd_transaction.hpp" @@ -1487,6 +1488,11 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(compiler_builtins_tests, T, validating_testers) tr // test test_ashrti3 CALL_TEST_FUNCTION( chain, "test_compiler_builtins", "test_ashrti3", {}); + chain.produce_block(); + chain.set_code( "testapi"_n, divmod_host_function_overflow_wast ); + chain.produce_block(); + CALL_TEST_FUNCTION( chain, "test_compiler_builtins", "", {}); //divmod_host_function_overflow_wast ignores action name + BOOST_REQUIRE_EQUAL( chain.validate(), true ); } FC_LOG_AND_RETHROW() diff --git a/unittests/contracts/test_wasts.hpp b/unittests/contracts/test_wasts.hpp index 94f5a9f249..b3f5a37cda 100644 --- a/unittests/contracts/test_wasts.hpp +++ b/unittests/contracts/test_wasts.hpp @@ -1001,4 +1001,40 @@ static const char set_jumbo_row_wast[] = R"=====( (drop (call $db_store_i64 (get_local $receiver) (get_local $receiver) (get_local $receiver) (i64.const 0) (i32.const 1) (i32.const 34603007))) ) ) +)====="; + +static const char divmod_host_function_overflow_wast[] = R"=====( +(module + (import "env" "__divti3" (func $__divti3 (param $ret_ptr i32) (param $la i64) (param $ha i64) (param $lb i64) (param $hb i64))) + (import "env" "__modti3" (func $__modti3 (param $ret_ptr i32) (param $la i64) (param $ha i64) (param $lb i64) (param $hb i64))) + + (memory $0 1) + (export "apply" (func $apply)) + + (func $apply (param $receiver i64) (param $account i64) (param $action_name i64) + (call $__divti3 (i32.const 8) + (i64.const 0) (i64.const 0x8000000000000000) ;; bytes: 0x00000000000000000000000000000080 (INT128_MIN) + (i64.const 0xffffffffffffffff) (i64.const 0xffffffffffffffff) ;; -1 + ) + ;;should still be bytes 00000000000000000000000000000080 + (if (i64.ne (i64.load (i32.const 8)) (i64.const 0)) (then + (unreachable) + )) + (if (i64.ne (i64.load (i32.const 16)) (i64.const 0x8000000000000000)) (then + (unreachable) + )) + + (call $__modti3 (i32.const 8) + (i64.const 0) (i64.const 0x8000000000000000) ;; bytes: 0x00000000000000000000000000000080 (INT128_MIN) + (i64.const 0xffffffffffffffff) (i64.const 0xffffffffffffffff) ;; -1 + ) + ;;should still be all 00s + (if (i64.ne (i64.load (i32.const 8)) (i64.const 0)) (then + (unreachable) + )) + (if (i64.ne (i64.load (i32.const 16)) (i64.const 0)) (then + (unreachable) + )) + ) +) )====="; \ No newline at end of file