diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -56,14 +56,14 @@ check_include_files("sys/prctl.h" HAVE_SYS_PRCTL_H) # Bitmanip intrinsics -function(check_builtin_exist SYMBOL VARIABLE) +function(check_builtin_exist SYMBOL VARIABLE ARGS) set( SOURCE_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckBuiltinExists.c" ) set( CMAKE_CONFIGURABLE_FILE_CONTENT - "int main(int argc, char** argv) { (void)argv; return ${SYMBOL}(argc); }\n" + "int main(int argc, char** argv) { (void)argv; return ${SYMBOL}(${ARGS}); }\n" ) configure_file( "${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in" @@ -101,10 +101,16 @@ endif() endfunction() -check_builtin_exist(__builtin_clz HAVE_DECL___BUILTIN_CLZ) -check_builtin_exist(__builtin_clzl HAVE_DECL___BUILTIN_CLZL) -check_builtin_exist(__builtin_clzll HAVE_DECL___BUILTIN_CLZLL) -check_builtin_exist(__builtin_popcount HAVE_DECL___BUILTIN_POPCOUNT) +check_builtin_exist(__builtin_clz HAVE_DECL___BUILTIN_CLZ "argc") +check_builtin_exist(__builtin_clzl HAVE_DECL___BUILTIN_CLZL "argc") +check_builtin_exist(__builtin_clzll HAVE_DECL___BUILTIN_CLZLL "argc") +check_builtin_exist(__builtin_popcount HAVE_DECL___BUILTIN_POPCOUNT "argc") +check_builtin_exist( + __builtin_saddll_overflow HAVE_DECL___BUILTIN_SADDLL_OVERFLOW + "argc, argc, (long long int*)&argc") +check_builtin_exist( + __builtin_ssubll_overflow HAVE_DECL___BUILTIN_SSUBLL_OVERFLOW + "argc, argc, (long long int*)&argc") # Memory management capabilities check_symbol_exists(M_ARENA_MAX "malloc.h" HAVE_MALLOPT_ARENA_MAX) diff --git a/src/config/bitcoin-config.h.cmake.in b/src/config/bitcoin-config.h.cmake.in --- a/src/config/bitcoin-config.h.cmake.in +++ b/src/config/bitcoin-config.h.cmake.in @@ -41,6 +41,8 @@ #cmakedefine HAVE_DECL___BUILTIN_CLZL 1 #cmakedefine HAVE_DECL___BUILTIN_CLZLL 1 #cmakedefine HAVE_DECL___BUILTIN_POPCOUNT 1 +#cmakedefine HAVE_DECL___BUILTIN_SADDLL_OVERFLOW 1 +#cmakedefine HAVE_DECL___BUILTIN_SSUBLL_OVERFLOW 1 #cmakedefine HAVE_MALLOPT_ARENA_MAX 1 #cmakedefine HAVE_MALLOC_INFO 1 diff --git a/src/script/script.h b/src/script/script.h --- a/src/script/script.h +++ b/src/script/script.h @@ -271,10 +271,18 @@ } inline CScriptNum operator+(const int64_t &rhs) const { - return CScriptNum(m_value + rhs); + int64_t result; + if (overflow_add_int64(m_value, rhs, result)) { + throw scriptnum_error("script number add overflow"); + } + return CScriptNum(result); } inline CScriptNum operator-(const int64_t &rhs) const { - return CScriptNum(m_value - rhs); + int64_t result; + if (overflow_sub_int64(m_value, rhs, result)) { + throw scriptnum_error("script number sub overflow"); + } + return CScriptNum(result); } inline CScriptNum operator+(const CScriptNum &rhs) const { return operator+(rhs.m_value); @@ -284,6 +292,10 @@ } inline CScriptNum operator/(const int64_t &rhs) const { + // Sanity check for invalid script num; should never occur + assert(m_value != std::numeric_limits::min()); + // Sanity check for division by 0; should be handled before + assert(rhs != 0); return CScriptNum(m_value / rhs); } inline CScriptNum operator/(const CScriptNum &rhs) const { @@ -291,6 +303,10 @@ } inline CScriptNum operator%(const int64_t &rhs) const { + // Sanity check for invalid script num; should never occur + assert(m_value != std::numeric_limits::min()); + // Sanity check for division by 0; should be handled before + assert(rhs != 0); return CScriptNum(m_value % rhs); } inline CScriptNum operator%(const CScriptNum &rhs) const { @@ -316,6 +332,7 @@ } inline CScriptNum operator-() const { + // Sanity check for invalid script num; should never occur assert(m_value != std::numeric_limits::min()); return CScriptNum(-m_value); } @@ -326,20 +343,20 @@ } inline CScriptNum &operator+=(const int64_t &rhs) { - assert( - rhs == 0 || - (rhs > 0 && m_value <= std::numeric_limits::max() - rhs) || - (rhs < 0 && m_value >= std::numeric_limits::min() - rhs)); - m_value += rhs; + int64_t result; + if (overflow_add_int64(m_value, rhs, result)) { + throw scriptnum_error("script number add overflow"); + } + m_value = result; return *this; } inline CScriptNum &operator-=(const int64_t &rhs) { - assert( - rhs == 0 || - (rhs > 0 && m_value >= std::numeric_limits::min() + rhs) || - (rhs < 0 && m_value <= std::numeric_limits::max() + rhs)); - m_value -= rhs; + int64_t result; + if (overflow_sub_int64(m_value, rhs, result)) { + throw scriptnum_error("script number sub overflow"); + } + m_value = result; return *this; } @@ -411,6 +428,66 @@ return result; } + /** + * Computes a + b and stores it in result. If the sum overflows or results + * in an invalid Script integer, return true, otherwise false. + */ + static bool overflow_add_int64(int64_t a, int64_t b, int64_t &result) { +#if HAVE_DECL___BUILTIN_SADDLL_OVERFLOW + static_assert(std::numeric_limits::max() == + std::numeric_limits::max()); + long long int tmp_result; // must use long long int due to types + if (__builtin_saddll_overflow(a, b, &tmp_result)) { + return true; + } + result = tmp_result; +#else + if (a > 0 && b > std::numeric_limits::max() - a) { + // integer overflow + return true; + } + if (a < 0 && b <= std::numeric_limits::min() - a) { + // integer underflow + return true; + } + result = a + b; +#endif + if (result == std::numeric_limits::min()) { + return true; + } + return false; + } + + /** + * Computes a - b and stores it in result. If the difference overflows or + * results in an invalid Script integer, return true, otherwise false. + */ + static bool overflow_sub_int64(int64_t a, int64_t b, int64_t &result) { +#if HAVE_DECL___BUILTIN_SSUBLL_OVERFLOW + static_assert(std::numeric_limits::max() == + std::numeric_limits::max()); + long long int tmp_result; // must use long long int due to types + if (__builtin_ssubll_overflow(a, b, &tmp_result)) { + return true; + } + result = tmp_result; +#else + if (a < 0 && b > std::numeric_limits::max() + a) { + // integer overflow + return true; + } + if (a > 0 && b <= std::numeric_limits::min() + a) { + // integer underflow + return true; + } + result = a - b; +#endif + if (result == std::numeric_limits::min()) { + return true; + } + return false; + } + int64_t m_value; }; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -195,6 +195,7 @@ script_standard_tests.cpp script_tests.cpp scriptnum_tests.cpp + scriptnum63bit_tests.cpp serialize_tests.cpp settings_tests.cpp sigcache_tests.cpp diff --git a/src/test/scriptnum63bit.h b/src/test/scriptnum63bit.h new file mode 100644 --- /dev/null +++ b/src/test/scriptnum63bit.h @@ -0,0 +1,96 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_TEST_SCRIPTNUM63BIT_H +#define BITCOIN_TEST_SCRIPTNUM63BIT_H + +#include + +namespace scriptnum_emulated { +#undef BITCOIN_SCRIPT_SCRIPT_H +#undef HAVE_DECL___BUILTIN_SADDLL_OVERFLOW +#undef HAVE_DECL___BUILTIN_SSUBLL_OVERFLOW +#include