Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions man/checkers/floatConversionOverflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# floatConversionOverflow

**Message**: Undefined behaviour: float (1e+100) to integer conversion overflow.<br/>
**Category**: Undefined Behaviour<br/>
**Severity**: Error/Warning<br/>
**Language**: C/C++

## Description

This checker uses ValueFlow analysis to detect conversions from a floating point value to an
integer type (via explicit cast, assignment or `return`) where the floating point value is outside
the range that the target integer type can represent, based on the platform's configured integer
widths.

## Motivation

Converting a floating point value to an integer type is undefined behaviour when the value does not
fit in the target type (for example, it is too large, too small, NaN, or infinite). Unlike integer
overflow, this cannot simply be assumed to "wrap around"; the actual result is unpredictable.

## Limitations / false negatives

- Code that ValueFlow determines is unreachable is not analyzed: a dead branch of a ternary
expression, or an operand of `&&`/`||` that short-circuit evaluation would never reach, is skipped
even though the cast is textually present:
```cpp
bool f(unsigned short x);
bool g() {
return false && f((unsigned short)75000.0); // not detected, right side never evaluated
}
```
- Detection depends on ValueFlow attaching a concrete floating point value to the expression; a value
that comes from a computation ValueFlow cannot bound is not checked.
- The generic (non-fast-path) bit-width check additionally requires a platform to be configured;
only the two `exp2`-based checks for extreme values run regardless of platform.

## How to fix

Make sure the floating point value fits in the target integer type before converting, for example by
clamping the value to the valid range, or by using a wider or floating point type instead.

Note: cppcheck only warns when ValueFlow can determine the actual floating point value (or a bound on
it). An unconstrained `double` parameter with no known or derivable value is not checked; the example
below uses a value that ValueFlow can trace so the warning actually fires.

Before:
```cpp
int32_t foo() {
double d = 1E100;
return (int32_t)d; // <- floatConversionOverflow, 1E100 does not fit in a 32-bit int
}
```

After:
```cpp
int32_t foo() {
double d = 1E100;
if (d > (double)INT32_MAX || d < (double)INT32_MIN)
return 0; // handle out-of-range value
return (int32_t)d;
}
```
75 changes: 75 additions & 0 deletions man/checkers/integerOverflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# integerOverflow

**Message**: Signed integer overflow for expression 'x*y'.<br/>
**Category**: Undefined Behaviour<br/>
**Severity**: Error/Warning<br/>
**Language**: C/C++

## Description

This checker uses ValueFlow analysis to detect when a signed integer arithmetic expression
(`+`, `-`, `*`, `/`, `<<`, etc.) can overflow or underflow the range of its result type, based on
the platform's configured integer widths (`int_bit`, `long_bit`, `long_long_bit`).

When the overflow/underflow only happens under a certain condition, the message explains that
"Either the condition ... is redundant or there is signed integer overflow/underflow ...".

As a special case, left-shifting into the sign bit (for example `1 << 31` for a 32-bit int) is not
reported, since this is common practice even though it is technically undefined behaviour; that is
instead covered by the [shiftTooManyBits](shiftTooManyBits.md) checker family.

## Motivation

Signed integer overflow is undefined behaviour in C and C++. In practice this often means the
calculation silently produces a wrong (wrapped or truncated) result, and with optimizations enabled
the compiler is allowed to assume overflow never happens, which can eliminate or reorder code in
surprising ways.

## Limitations / false negatives

- This checker only looks at expressions whose result type is `int`, `long` or `long long` **and**
signed. Unsigned overflow (wraparound) is well-defined behaviour in C/C++ and is intentionally not
reported here.
- **Left-shifts into the sign bit are deliberately not reported by this checker**, even though they
are technically a signed integer overflow, because this is common practice (for example
`1 << 31` for a 32-bit `int`). Such shifts are instead the responsibility of the
[shiftTooManyBits](shiftTooManyBits.md) checker family. This means the same expression can trigger
`shiftTooManyBitsSigned` without also triggering `integerOverflow`:
```cpp
int f(int i) {
return (i == 31) ? 1 << i : 0; // only reported as shiftTooManyBitsSigned, not integerOverflow
}
```
- This checker requires a platform to be configured, and is skipped when the platform's `int` width
is already as wide as cppcheck's internal integer representation.
- Detection depends on ValueFlow computing a concrete or condition-derived out-of-range value for the
expression; not every expression that can overflow gets such a value, so some real overflows can be
missed.

## How to fix

You can fix these warnings by:
1. Using a wider integer type for the calculation
2. Rewriting the calculation to avoid the overflow (for example checking bounds before multiplying)
3. Using an unsigned type, if wraparound behaviour is actually intended

Note: cppcheck only warns when ValueFlow can actually determine that the calculation overflows -
either from a known value (as below) or from a condition elsewhere in the code (see
`integerOverflowCond` above). A plain `a * b` of two otherwise-unconstrained parameters does not by
itself give ValueFlow anything to prove an overflow with, so it is not reported.

Before:
```cpp
int32_t f() {
int32_t intmax = 0x7fffffff; // INT32_MAX
return intmax + 1; // <- integerOverflow, known to overflow 32-bit int
}
```

After:
```cpp
int64_t f() {
int32_t intmax = 0x7fffffff;
return (int64_t)intmax + 1; // <- widen before adding
}
```
64 changes: 64 additions & 0 deletions man/checkers/shiftTooManyBits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# shiftTooManyBits and shiftTooManyBitsSigned

**Message**: Shifting 32-bit value by 40 bits is undefined behaviour<br/>
**Category**: Undefined Behaviour<br/>
**Severity**: Error/Warning<br/>
**Language**: C/C++

## Description

This checker warns when a bitwise shift (`<<`, `>>`, `<<=`, `>>=`) shifts a value by a number of bits
that is greater than or equal to the width of the (promoted) left-hand side type.

There are two related warnings:
- `shiftTooManyBits`: the shift amount is greater than or equal to the bit width of the type. This is
undefined behaviour according to the C/C++ standard.
- `shiftTooManyBitsSigned`: the left-hand side type is signed and the shift amount is exactly
`bits - 1`. Shifting a signed type this far is undefined behaviour before C++14, and
implementation-defined behaviour from C++14 onwards.

The number of bits of the left-hand side type is determined from the platform settings
(`int_bit`, `long_bit`, `long_long_bit`), so this checker requires a platform to be configured.

## Motivation

Shifting a value by more bits than its type contains is undefined behaviour. The result is
unpredictable and can vary between compilers, compiler versions and optimization settings.

## Limitations / false negatives

- **Uppercase macro-like calls are skipped entirely.** A statement of the form `NAME(...)` where
`NAME` is all-uppercase and not a known function is treated as a macro invocation and the whole
call is skipped, so a bad shift inside it is not detected, for example:
```cpp
void f(unsigned int x) {
UINFO(x << 1234); // not detected
}
```
- Only applies when the left-hand side type is a non-pointer integral type that resolves to `int`,
`long` or `long long` width; other cases (for example pointer types) are not checked.
- This checker relies on ValueFlow to prove that the shift amount is out of range. When the shift
amount is guarded by several combined conditions, ValueFlow may not be able to derive a tight
enough bound, and the warning can be missed even though the underlying issue is real.
- Code that ValueFlow determines is unreachable (for example a branch that can never be taken due to
a constant/template condition) is not analyzed, so a bad shift in genuinely dead code is not
reported.

## How to fix

Make sure the shift amount is smaller than the bit width of the left-hand side type. This often means
casting the left-hand side to a wider type before shifting, or fixing a wrong shift amount.

Before:
```cpp
int32_t foo(int32_t x) {
return x << 40; // <- shiftTooManyBits, 'int' is only 32 bits
}
```

After:
```cpp
int64_t foo(int32_t x) {
return (int64_t)x << 40; // <- widen the operand before shifting
}
```
87 changes: 87 additions & 0 deletions man/checkers/signConversion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# signConversion

**Message**: Expression 'x' can have a negative value. That is converted to an unsigned value and used in an unsigned calculation.<br/>
**Category**: Type Safety<br/>
**Severity**: Warning<br/>
**Language**: C/C++

## Description

This checker uses ValueFlow analysis to detect arithmetic expressions (other than `+`/`-`) whose
result type is unsigned, where one of the operands can have a negative value. When that happens,
the negative operand is implicitly converted to an unsigned value before the calculation, which can
produce a very large value instead of the intended negative one.

If the negative value is a known constant, the message states the operand "has" a negative value;
otherwise it states the operand "can have" a negative value, based on ValueFlow analysis.

This checker only runs when the `warning` severity is enabled.

## Motivation

Implicit conversion of a negative value to an unsigned type is well-defined (it wraps around to a
large positive value), but it rarely matches programmer intent and is a common source of bugs, for
example in loop conditions, index calculations and buffer size calculations.

## Design note: `+` and `-` are intentionally not checked

This checker does not warn about `+` and `-`, even though the same implicit negative-to-unsigned
conversion happens there too. This is intentional, not an oversight: for `+` and `-`, adding an
explicit cast would not change the computed result at all.

```cpp
void f(int x) { // x can be negative
unsigned int a = x + 5U; // implicit conversion
unsigned int b = (unsigned int)x + 5U; // explicit cast - identical result
}
```

Two's-complement addition and subtraction commute with truncation to an unsigned width, so the
implicit conversion already computes exactly what an explicit `(unsigned int)` cast would. There is
nothing for an explicit cast to fix or clarify, so a warning here would not be actionable. Other
arithmetic operators (`*`, `/`, `%`, shifts, etc.) do not have this property in the same way and are
still checked.

## Limitations / false negatives

- Only the direct operands of the unsigned arithmetic operator are examined; a negative value that
is only reachable through a deeper subexpression is not specifically traced beyond what ValueFlow
already attaches to that immediate operand.
- Detection depends on ValueFlow having a possible or known negative value for the operand; an
unconstrained parameter with no usable value information will not be flagged.

## How to fix

You can fix these warnings by:
1. Making sure the operand cannot be negative in that context (fix upstream logic)
2. Using a signed type for the calculation
3. Adding an explicit check or cast to make the intended behaviour clear

Note: cppcheck only warns when ValueFlow can actually determine that the operand can be negative -
either from a known/possible value at the call site (as below), or from a condition earlier in the
same function. A plain `int` parameter with no callers and no surrounding condition gives ValueFlow
no evidence that it can be negative, so it is not reported.

Before:
```cpp
unsigned int calcSize(int count, unsigned int itemSize) {
return count * itemSize; // <- signConversion, count can have a negative value
}

void caller() {
calcSize(-4, 4);
}
```

After:
```cpp
unsigned int calcSize(int count, unsigned int itemSize) {
if (count < 0)
return 0;
return count * itemSize;
}

void caller() {
calcSize(-4, 4);
}
```
2 changes: 1 addition & 1 deletion man/checkers/truncLongCast.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ After (change type of assigned variable):
void foo(int32_t y) {
int32_t x = y * y;
}
```
```