V1083. Signed integer overflow in arithmetic expression. This leads to undefined behavior.
The analyzer has detected an arithmetic expression where a signed integer overflow may occur.
The example:
long long foo()
{
long longOperand = 0x7FFF'FFFF;
long long y = longOperand * 0xFFFF;
return y;
}
According to the C and C++ rules, the result type of the longOperand * 0xFFFF
expression will be long
. On Windows, when using the MSVC compiler, the long
type is 4 bytes in size. The maximum value that can be represented by this type is 2'147'483'647 in decimal or 0x7FFF'FFFF in hexadecimal. When multiplying the longOperand
variable by 0xFFFF (65,535), the 0x7FFF'7FFF'0001 result is expected. However, as specified in the C18 standard (section 6.5, paragraph 5) and C++20 (section 7.1, paragraph 4), the signed integer overflow leads to undefined behavior.
There are several ways to fix this code.
To evaluate expressions correctly, use types with a size large enough to hold the expected value. If the value does not fit a machine word, use one of the libraries for arbitrary-precision arithmetic, such as GMP, MPRF, and cnl.
The code fragment above can be fixed as follows:
long long foo()
{
long longOperand = 0x7FFF'FFFF;
long long y = static_cast<long long>(longOperand) * 0xFFFF;
return y;
}
To handle the signed integer overflow as an unintended behavior, use specialized libraries that provide safe integer operations, such as boost::safe_numerics or Google Integers.
To implement wraparound arithmetic for signed integers with standard-defined behavior, use unsigned integers for evaluations. When the unsigned integer overflow occurs, the integer wraps around modulo 2 ^ n
, where n
is the number of bits within the integer.
The possible solution based on std::bit_cast
(C++20):
#include <concepts>
#include <type_traits>
#include <bit>
#include <functional>
namespace detail
{
template <std::signed_integral R,
std::signed_integral T1,
std::signed_integral T2,
std::invocable<std::make_unsigned_t<T1>,
std::make_unsigned_t<T2>> Fn>
R safe_signed_wrapper(T1 lhs, T2 rhs, Fn &&op)
noexcept(std::is_nothrow_invocable_v<Fn,
std::make_unsigned_t<T1>,
std::make_unsigned_t<T2>>)
{
auto uLhs = std::bit_cast<std::make_unsigned_t<T1>>(lhs);
auto uRhs = std::bit_cast<std::make_unsigned_t<T2>>(rhs);
auto res = std::invoke(std::forward<Fn>(op), uLhs, uRhs);
using UR = std::make_unsigned_t<R>;
return std::bit_cast<R>(static_cast<UR>(res));
}
}
The std::bit_cast
function converts lhs
and rhs
to the corresponding unsigned representations. Next, some arithmetic operation is performed on the two converted operands. After that, the result is expanded or narrowed to the required result type and converted to a signed one.
With this approach, signed integers follow the semantics of unsigned ones in arithmetic operations. This does not lead to undefined behavior.
For example, you can see here that the compiler may optimize the code if it detects the signed integer overflow.
The example:
bool is_max_int(int32_t a)
{
return a + 1 < a;
}
If a
equals MAX_INT
, the a + 1 < a
condition will be false
. This is a way to check whether an overflow has occurred. However, the compiler generates the following code:
is_max_int(int): # @is_max_int(int)
xor eax, eax
ret
The assembly xor eax, eax
instruction resets the result of the is_max_int
function execution. As a result, the latter function always returns true
, no matter what the a
value has. In this case, this is the result of undefined behavior due to overflow.
In the case of an unsigned representation, undefined behavior does not occur:
is_max_int(int): # @is_max_int(int)
cmp edi, 2147483647
sete al
ret
The compiler has generated code that does check the condition.
Note. The diagnostic rule supports a special setting that enables the analyzer to detect overflow in both signed and unsigned numeric types.
To enable this setting, add the following comment to the source code or to the diagnostic rules configuration file (.pvsconfig
):
//+V1083 CHECK_UNSIGNED_OVERFLOW:YES
To disable the setting, add the comment:
//+V1083 CHECK_UNSIGNED_OVERFLOW:NO
This diagnostic is classified as:
|
You can look at examples of errors detected by the V1083 diagnostic. |