mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
math: Avoid signed integer overflow when intrinsics are available
The `math(EXPR)` command documents that results must fit in a 64-bit signed integer. When built with compilers that offer the relevant intrinsics, detect overflow and issue warnings.
This commit is contained in:
@@ -106,6 +106,7 @@ set(CMake_TEST_IPO_WORKS_C "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_IPO_WORKS_CXX "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_IPO_WORKS_Fortran "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "")
|
||||
set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "")
|
||||
set(CMake_TEST_Qt5 "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
|
||||
|
||||
@@ -106,6 +106,7 @@ set(CMake_TEST_IPO_WORKS_C "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_IPO_WORKS_CXX "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_IPO_WORKS_Fortran "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "")
|
||||
set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "")
|
||||
set(CMake_TEST_Qt5 "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
|
||||
|
||||
@@ -12,6 +12,7 @@ set(CMAKE_SKIP_BOOTSTRAP_TEST TRUE CACHE BOOL "")
|
||||
set(ENV{KWSYS_TEST_PROCESS_1_COUNT} 11)
|
||||
|
||||
set(CMake_TEST_GUI "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "")
|
||||
set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
set(CMake_TEST_GUI "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "")
|
||||
set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
|
||||
|
||||
@@ -109,6 +109,7 @@ if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
|
||||
set(CMake_TEST_ISPC "ON" CACHE STRING "")
|
||||
endif()
|
||||
set(CMake_TEST_LOCALE_CHARSET "ISO-8859-1" CACHE STRING "")
|
||||
set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "")
|
||||
set(CMake_TEST_Qt5 "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
|
||||
|
||||
@@ -5,6 +5,7 @@ if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
|
||||
set(CMake_TEST_ISPC "ON" CACHE STRING "")
|
||||
endif()
|
||||
set(CMake_TEST_LOCALE_CHARSET "UTF-8" CACHE STRING "")
|
||||
set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "")
|
||||
set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly,build_database,import_std23" CACHE STRING "")
|
||||
set(CMake_TEST_Rust "ON" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
|
||||
|
||||
@@ -7,6 +7,7 @@ set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_GUI "ON" CACHE BOOL "")
|
||||
set(CMake_TEST_LOCALE_CHARSET "UTF-8" CACHE STRING "")
|
||||
set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERSION "1.2" CACHE STRING "")
|
||||
|
||||
@@ -6,6 +6,7 @@ set(CMake_TEST_GUI "ON" CACHE BOOL "")
|
||||
if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
|
||||
set(CMake_TEST_ISPC "ON" CACHE STRING "")
|
||||
endif()
|
||||
set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERSION "1.2" CACHE STRING "")
|
||||
|
||||
@@ -10,6 +10,7 @@ if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
|
||||
set(CMake_TEST_ISPC "ON" CACHE STRING "")
|
||||
endif()
|
||||
set(CMake_TEST_LOCALE_CHARSET "UTF-8" CACHE STRING "")
|
||||
set(CMake_TEST_MATH_OVERFLOW ON CACHE BOOL "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
|
||||
set(CMake_TEST_TLS_VERSION "1.2" CACHE STRING "")
|
||||
|
||||
@@ -100,6 +100,11 @@ void cmExprParserHelper::Error(char const* str)
|
||||
this->ErrorString = ostr.str();
|
||||
}
|
||||
|
||||
void cmExprParserHelper::Warning(std::string str)
|
||||
{
|
||||
this->WarningString = cmStrCat(this->WarningString, std::move(str), '\n');
|
||||
}
|
||||
|
||||
void cmExprParserHelper::UnexpectedChar(char c)
|
||||
{
|
||||
unsigned long pos = static_cast<unsigned long>(this->InputBufferPos);
|
||||
@@ -121,27 +126,63 @@ void cmExprParserHelper::SetError(std::string errorString)
|
||||
|
||||
std::int64_t cmExprParserHelper::ShL(std::int64_t l, std::int64_t r)
|
||||
{
|
||||
return l << r;
|
||||
if (l < 0) {
|
||||
this->Warning(
|
||||
cmStrCat("left shift of negative value in:\n ", l, " << ", r));
|
||||
}
|
||||
if (r < 0) {
|
||||
this->Warning(
|
||||
cmStrCat("shift exponent is negative in:\n ", l, " << ", r));
|
||||
r &= 0x3F;
|
||||
}
|
||||
if (r >= 64) {
|
||||
this->Warning(
|
||||
cmStrCat("shift exponent is too large in:\n ", l, " << ", r));
|
||||
r &= 0x3F;
|
||||
}
|
||||
return static_cast<std::int64_t>(static_cast<std::uint64_t>(l) << r);
|
||||
}
|
||||
|
||||
std::int64_t cmExprParserHelper::ShR(std::int64_t l, std::int64_t r)
|
||||
{
|
||||
if (r < 0) {
|
||||
this->Warning(
|
||||
cmStrCat("shift exponent is negative in:\n ", l, " >> ", r));
|
||||
r &= 0x3F;
|
||||
}
|
||||
if (r >= 64) {
|
||||
this->Warning(
|
||||
cmStrCat("shift exponent is too large in:\n ", l, " >> ", r));
|
||||
r &= 0x3F;
|
||||
}
|
||||
return l >> r;
|
||||
}
|
||||
|
||||
std::int64_t cmExprParserHelper::Add(std::int64_t l, std::int64_t r)
|
||||
{
|
||||
return l + r;
|
||||
std::int64_t sum;
|
||||
if (this->AddOverflow(l, r, &sum)) {
|
||||
this->Warning(cmStrCat("signed integer overflow in:\n ", l, " + ", r));
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
std::int64_t cmExprParserHelper::Sub(std::int64_t l, std::int64_t r)
|
||||
{
|
||||
return l - r;
|
||||
std::int64_t diff;
|
||||
if (this->SubOverflow(l, r, &diff)) {
|
||||
this->Warning(cmStrCat("signed integer overflow in:\n ", l, " - ", r));
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
std::int64_t cmExprParserHelper::Mul(std::int64_t l, std::int64_t r)
|
||||
{
|
||||
return l * r;
|
||||
std::int64_t prod;
|
||||
if (this->MulOverflow(l, r, &prod)) {
|
||||
this->Warning(cmStrCat("signed integer overflow in:\n ", l, " * ", r));
|
||||
}
|
||||
return prod;
|
||||
}
|
||||
|
||||
std::int64_t cmExprParserHelper::Div(std::int64_t l, std::int64_t r)
|
||||
@@ -159,3 +200,73 @@ std::int64_t cmExprParserHelper::Mod(std::int64_t l, std::int64_t r)
|
||||
}
|
||||
return l % r;
|
||||
}
|
||||
|
||||
// The __has_builtin preprocessor check was added in Clang 2.6 and GCC 10.
|
||||
// The __builtin_X_overflow intrinsics were added in Clang 3.4 and GCC 5.
|
||||
#ifndef __has_builtin
|
||||
# if defined(__GNUC__) && __GNUC__ >= 5
|
||||
# define __has_builtin(x) 1
|
||||
# else
|
||||
# define __has_builtin(x) 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
bool cmExprParserHelper::AddOverflow(long l, long r, long* p)
|
||||
{
|
||||
#if __has_builtin(__builtin_saddl_overflow)
|
||||
return __builtin_saddl_overflow(l, r, p);
|
||||
#else
|
||||
*p = l + r;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cmExprParserHelper::AddOverflow(long long l, long long r, long long* p)
|
||||
{
|
||||
#if __has_builtin(__builtin_saddll_overflow)
|
||||
return __builtin_saddll_overflow(l, r, p);
|
||||
#else
|
||||
*p = l + r;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cmExprParserHelper::SubOverflow(long l, long r, long* p)
|
||||
{
|
||||
#if __has_builtin(__builtin_ssubl_overflow)
|
||||
return __builtin_ssubl_overflow(l, r, p);
|
||||
#else
|
||||
*p = l - r;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cmExprParserHelper::SubOverflow(long long l, long long r, long long* p)
|
||||
{
|
||||
#if __has_builtin(__builtin_ssubll_overflow)
|
||||
return __builtin_ssubll_overflow(l, r, p);
|
||||
#else
|
||||
*p = l - r;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cmExprParserHelper::MulOverflow(long l, long r, long* p)
|
||||
{
|
||||
#if __has_builtin(__builtin_smull_overflow)
|
||||
return __builtin_smull_overflow(l, r, p);
|
||||
#else
|
||||
*p = l * r;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cmExprParserHelper::MulOverflow(long long l, long long r, long long* p)
|
||||
{
|
||||
#if __has_builtin(__builtin_smulll_overflow)
|
||||
return __builtin_smulll_overflow(l, r, p);
|
||||
#else
|
||||
*p = l * r;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
|
||||
int LexInput(char* buf, int maxlen);
|
||||
void Error(char const* str);
|
||||
void Warning(std::string str);
|
||||
|
||||
void SetResult(std::int64_t value);
|
||||
|
||||
@@ -42,6 +43,13 @@ public:
|
||||
std::int64_t Div(std::int64_t l, std::int64_t r);
|
||||
std::int64_t Mod(std::int64_t l, std::int64_t r);
|
||||
|
||||
static bool AddOverflow(long l, long r, long* p);
|
||||
static bool AddOverflow(long long l, long long r, long long* p);
|
||||
static bool SubOverflow(long l, long r, long* p);
|
||||
static bool SubOverflow(long long l, long long r, long long* p);
|
||||
static bool MulOverflow(long l, long r, long* p);
|
||||
static bool MulOverflow(long long l, long long r, long long* p);
|
||||
|
||||
private:
|
||||
std::string::size_type InputBufferPos;
|
||||
std::string InputBuffer;
|
||||
|
||||
@@ -769,6 +769,9 @@ add_RunCMake_test(include_directories)
|
||||
add_RunCMake_test(include_guard)
|
||||
add_RunCMake_test(list)
|
||||
add_RunCMake_test(load_cache)
|
||||
if(DEFINED CMake_TEST_MATH_OVERFLOW)
|
||||
list(APPEND math_ARGS -DCMake_TEST_MATH_OVERFLOW=${CMake_TEST_MATH_OVERFLOW})
|
||||
endif()
|
||||
add_RunCMake_test(math)
|
||||
add_RunCMake_test(message)
|
||||
add_RunCMake_test(option)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
^CMake Warning \(dev\) at [^
|
||||
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\):
|
||||
left shift of negative value in:
|
||||
|
||||
-4 << 1
|
||||
|
||||
This warning is for project developers\. Use -Wno-dev to suppress it\.
|
||||
+
|
||||
CMake Warning \(dev\) at [^
|
||||
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\):
|
||||
shift exponent is negative in:
|
||||
|
||||
4 << -63
|
||||
|
||||
This warning is for project developers\. Use -Wno-dev to suppress it\.
|
||||
+
|
||||
CMake Warning \(dev\) at [^
|
||||
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\):
|
||||
shift exponent is negative in:
|
||||
|
||||
4 >> -63
|
||||
|
||||
This warning is for project developers\. Use -Wno-dev to suppress it\.
|
||||
+
|
||||
CMake Warning \(dev\) at [^
|
||||
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\):
|
||||
shift exponent is too large in:
|
||||
|
||||
4 << 65
|
||||
|
||||
This warning is for project developers\. Use -Wno-dev to suppress it\.
|
||||
+
|
||||
CMake Warning \(dev\) at [^
|
||||
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\):
|
||||
shift exponent is too large in:
|
||||
|
||||
4 >> 65
|
||||
|
||||
This warning is for project developers\. Use -Wno-dev to suppress it\.
|
||||
+
|
||||
CMake Warning \(dev\) at [^
|
||||
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\):
|
||||
signed integer overflow in:
|
||||
|
||||
9223372036854775807 \+ 1
|
||||
|
||||
This warning is for project developers\. Use -Wno-dev to suppress it\.
|
||||
+
|
||||
CMake Warning \(dev\) at [^
|
||||
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\):
|
||||
signed integer overflow in:
|
||||
|
||||
-9223372036854775807 - 2
|
||||
|
||||
This warning is for project developers\. Use -Wno-dev to suppress it\.
|
||||
+
|
||||
CMake Warning \(dev\) at [^
|
||||
]+/Tests/RunCMake/math/Overflow\.cmake:[0-9]+ \(math\):
|
||||
signed integer overflow in:
|
||||
|
||||
9223372036854775807 \* 2
|
||||
|
||||
This warning is for project developers\. Use -Wno-dev to suppress it\.$
|
||||
@@ -0,0 +1,9 @@
|
||||
^-- -4 << 1: -8
|
||||
-- -4 >> 1: -2
|
||||
-- 4 << -63: 8
|
||||
-- 4 >> -63: 2
|
||||
-- 4 << 65: 8
|
||||
-- 4 >> 65: 2
|
||||
-- 0x7FFFFFFFFFFFFFFF \+ 1: -9223372036854775808
|
||||
-- -0x7FFFFFFFFFFFFFFF - 2: 9223372036854775807
|
||||
-- 0x7FFFFFFFFFFFFFFF \* 2: -2$
|
||||
@@ -0,0 +1,14 @@
|
||||
foreach(expr IN ITEMS
|
||||
"-4 << 1"
|
||||
"-4 >> 1"
|
||||
" 4 << -63"
|
||||
" 4 >> -63"
|
||||
" 4 << 65"
|
||||
" 4 >> 65"
|
||||
" 0x7FFFFFFFFFFFFFFF + 1"
|
||||
"-0x7FFFFFFFFFFFFFFF - 2"
|
||||
" 0x7FFFFFFFFFFFFFFF * 2"
|
||||
)
|
||||
math(EXPR result "${expr}")
|
||||
message(STATUS "${expr}: ${result}")
|
||||
endforeach()
|
||||
@@ -8,3 +8,7 @@ run_cmake(MATH-InvalidExpression)
|
||||
run_cmake(MATH-ToleratedExpression)
|
||||
run_cmake(MATH-DivideByZero)
|
||||
run_cmake(MATH-ModByZero)
|
||||
|
||||
if(CMake_TEST_MATH_OVERFLOW)
|
||||
run_cmake_script(Overflow)
|
||||
endif()
|
||||
|
||||
Reference in New Issue
Block a user