Main Content

CWE Rule 191

Integer Underflow (Wrap or Wraparound)

Since R2023a

Description

Rule Description

The product subtracts one value from another, such that the result is less than the minimum allowable integer value, which produces a value that is not equal to the correct result.

Polyspace Implementation

The rule checker checks for these issues:

  • Integer constant overflow

  • Integer overflow

  • Unsigned integer constant overflow

  • Unsigned integer conversion overflow

  • Unsigned integer overflow

Examples

expand all

Issue

This issue occurs in the following cases:

  • You assign a compile-time integer constant to a signed integer variable whose data type cannot accommodate the value.

    See Constant Overflows from Assignments.

  • You use an enum value that cannot be accommodated by the underlying type of the enum (and the underlying type is signed). For most C compilers, the default underlying type is signed int (based on the C standard).

    See Constant Overflows from enum Values.

  • You perform a binary operation involving two integer constants that results in an overflow, that is, a value outside the range allowed by the data type that the operation uses. A binary operation with integer constants uses the signed int data type (unless you use modifiers such as u or L).

    See Constant Overflows from Binary Operations.

An n-bit signed integer holds values in the range [-2n-1, 2n-1-1]. For instance, c is an 8-bit signed char variable that cannot hold the value 255.

signed char c = 255;

This defect checker depends on the following options:

You do not see the defect in these situations:

  • Creation of new constants from const variables (for specific compilers only).

    Different compilers might define compile-time constants differently. In the following code, c+1 is considered a compile-time constant by GCC compilers, but not by the standard C compiler:

    const int16_t c = 32767;
    int16_t y = c + 1;
    Whether you see a violation of this check on y might depend on your compiler.

  • Bitwise NOT operation.

    Polyspace® does not raise this violation when you perform a bitwise NOT operation.

Risk

The default behavior for constant overflows can vary between compilers and platforms. Retaining constant overflows can reduce the portability of your code.

Even if your compilers wraps around overflowing constants with a warning, the wrap-around behavior can be unintended and cause unexpected results.

Fix

Check if the constant value is what you intended. If the value is correct, use a different, possibly wider, data type for the variable.

Example — Constant Overflows from Assignments
#define MAX_UNSIGNED_CHAR 255 //Noncompliant
#define MAX_SIGNED_CHAR 127 //Noncompliant

void main() {
    char c1 = MAX_UNSIGNED_CHAR;
    char c2 = MAX_SIGNED_CHAR+1;
}

In this example, the defect appears on the macros because at least one use of the macro causes an overflow. To reproduce these defects, use a Target processor type (-target) where char is signed by default.

Correction — Use Different Data Type

One possible correction is to use a different data type for the variables that overflow.

#define MAX_UNSIGNED_CHAR 255 
#define MAX_SIGNED_CHAR 127

void main() {
    unsigned char c1 = MAX_UNSIGNED_CHAR;
    unsigned char c2 = MAX_SIGNED_CHAR+1;
}
Example — Constant Overflows from enum Values
enum {
  a=0x7fffffff,
  b,  //Noncompliant
  c=0xffffffff  //Noncompliant
} MyEnumA;

In this example, the enum has an underlying type int. The int type can accommodate values in the range [-231, 231-1]. However, the value of the enumerator b is 0x80000000 or 231 (one more than the previous value a). This value falls outside the allowed range of int.

The value of c, that is 0xffffffff or 232-1, is even larger and also causes an overflow.

To see this defect:

Example — Constant Overflows from Binary Operations
const unsigned int K_ATM_Label_Ram_init_value [] = {
     0x06 | ( 3 << 29) ,
     0x80 | ( 9 << 29) , //Noncompliant
     ( 2 << 31 )  //Noncompliant
};

In this example, two of the shift operations result in values that cannot be accommodated by the signed int data type. The signed int data type can accommodate values in the range [-231, 231-1]. The operation:

  • 9 << 29 results in the value 232+536870912.

  • 2 << 31 results in the value 232.

Note that even though the result is assigned to an unsigned int variable, the overflow detection uses the underlying type of the binary operation, that is, signed int.

Issue

This issue occurs when an operation on integer variables results in values that cannot be represented by the data type that the operation uses. This data type depends on the operand types and determines the number of bytes allocated for storing the result, thus constraining the range of allowed values.

Note that:

  • The data type used to determine an overflow is based on the operand data types. If you then assign the result of an operation to another variable, a different checker, Integer conversion overflow, determines if the value assigned also overflows the variable assigned to. For instance, in an operation such as:

    res = x + y;
    This checker checks for an overflow based on the types of x and y, and not on the type of res. The checker for integer conversion overflows then checks for an overflow based on the type of res.

  • The two operands in a binary operation might undergo promotion before the operation occurs. See also Code Prover Assumptions About Implicit Data Type Conversions (Polyspace Code Prover).

The exact storage allocation for different data types depends on your processor. See Target processor type (-target).

Risk

Integer overflows on signed integers result in undefined behavior.

Fix

The fix depends on the root cause of the defect. Often the result details (or source code tooltips in Polyspace as You Code) show a sequence of events that led to the defect. You can implement the fix on any event in the sequence. If the result details do not show this event history, you can search for previous references of variables relevant to the defect using right-click options in the source code and find related events. See also Interpret Bug Finder Results in Polyspace Desktop User Interface or Interpret Bug Finder Results in Polyspace Access Web Interface (Polyspace Access).

You can fix the defect by:

  • Using a bigger data type for the result of the operation so that all values can be accommodated.

  • Checking for values that lead to the overflow and performing appropriate error handling.

To avoid overflows in general, try one of these techniques:

  • Keep integer variable values restricted to within half the range of signed integers.

  • In operations that might overflow, check for conditions that can lead to the overflow and implement wrap around or saturation behavior depending on how the result of the operation is used. The result then becomes predictable and can be safely used in subsequent computations.

See examples of fixes below.

If you do not want to fix the issue, add comments to your result or code to avoid another review. See:

Extend Checker

A default Bug Finder analysis might not raise this defect when the input values are unknown and only a subset of inputs cause an issue. To check for defects caused by specific system input values, run a stricter Bug Finder analysis. See Extend Bug Finder Checkers to Find Defects from Specific System Input Values.

Example — Addition of Maximum Integer
#include <limits.h>

int plusplus(void) {

    int var = INT_MAX;
    var++;  //Noncompliant
    return var;
}

In the third statement of this function, the variable var is increased by one. But the value of var is the maximum integer value, so an int cannot represent one plus the maximum integer value.

Correction — Different Storage Type

One possible correction is to change data types. Store the result of the operation in a larger data type (Note that on a 32-bit machine, int and long has the same size). In this example, on a 32-bit machine, by returning a long long instead of an int, the overflow error is fixed.

#include <limits.h>

long long plusplus(void) {

    long long lvar = INT_MAX;
    lvar++;
    return lvar;
}
Issue

This issue occurs in the following cases:

  • You assign a compile-time constant to an unsigned integer variable whose data type cannot accommodate the value.

  • You use an enum value that cannot be accommodated by the underlying type of the enum (and the underlying type is unsigned).

An n-bit unsigned integer holds values in the range [0, 2n-1]. For instance, c is an 8-bit unsigned char variable that cannot hold the value 256.

unsigned char c = 256;

This defect checker depends on the following options:

You do not see the defect in these situations:

  • Creation of new constants from const variables (for specific compilers only).

    Different compilers might define compile-time constants differently. In the following code, c+1u is considered a compile time-constant by GCC compilers, but not by the standard C compiler:

    const uint16_t c = 0xffffu;
    uint16_t y = c + 1u;
    Whether you see a violation of this check on y might depend on your compiler.

  • Bitwise NOT operation.

    Polyspace does not raise this violation when you perform a bitwise NOT operation.

Risk

The C standard states that overflowing unsigned integers must be wrapped around (see, for instance, the C11 standard, section 6.2.5). However, the wrap-around behavior can be unintended and cause unexpected results.

Fix

Check if the constant value is what you intended. If the value is correct, use a wider data type for the variable.

Example — Overflows from Assignments
#define MAX_UNSIGNED_CHAR 255  //Noncompliant
#define MAX_UNSIGNED_SHORT 65535  //Noncompliant

void main() {
    unsigned char c1 = MAX_UNSIGNED_CHAR + 1;
    unsigned short c2 = MAX_UNSIGNED_SHORT + 1;
}

In this example, the defect appears on the macros because at least one use of the macro causes an overflow.

Correction — Use Wider Data Type

One possible correction is to use a wider data type for the variables that overflow.

#define MAX_UNSIGNED_CHAR 255
#define MAX_UNSIGNED_SHORT 65535

void main() {
    unsigned short c1 = MAX_UNSIGNED_CHAR + 1;
    unsigned int c2 = MAX_UNSIGNED_SHORT + 1;
}
Issue

This issue occurs when converting an unsigned integer to a smaller unsigned integer type. If the variable does not have enough bytes to represent the original constant, the conversion overflows.

The exact storage allocation for different floating point types depends on your processor. See Target processor type (-target).

Risk

Integer conversion overflows result in undefined behavior.

Fix

The fix depends on the root cause of the defect. Often the result details (or source code tooltips in Polyspace as You Code) show a sequence of events that led to the defect. You can implement the fix on any event in the sequence. If the result details do not show this event history, you can search for previous references of variables relevant to the defect using right-click options in the source code and find related events. See also Interpret Bug Finder Results in Polyspace Desktop User Interface or Interpret Bug Finder Results in Polyspace Access Web Interface (Polyspace Access).

You can fix the defect by:

  • Using a bigger data type for the result of the conversion so that all values can be accommodated.

  • Checking for values that lead to the overflow and performing appropriate error handling.

In general, avoid conversions to smaller integer types.

See examples of fixes below.

If you do not want to fix the issue, add comments to your result or code to avoid another review. See:

Extend Checker

A default Bug Finder analysis might not raise this defect when the input values are unknown and only a subset of inputs cause an issue. To check for defects caused by specific system input values, run a stricter Bug Finder analysis. See Extend Bug Finder Checkers to Find Defects from Specific System Input Values.

Example — Converting from int to char
unsigned char convert(void) {
    unsigned int unum = 1000000U;

    return (unsigned char)unum;  //Noncompliant
}

In the return statement, the unsigned integer variable unum is converted to an unsigned character type. However, the conversion overflows because 1000000 requires at least 20 bits. The C programming language standard does not view unsigned overflow as an error because the program automatically reduces the result by modulo the maximum value plus 1. In this example, unum is reduced by modulo 2^8 because a character data type can only represent 2^8-1.

Correction — Change Conversion Type

One possible correction is to convert to a different integer type that can represent the entire number. For example, long.

unsigned long convert(void) {
    unsigned int unum = 1000000U;

    return (unsigned long)unum;  
}
Issue

This issue occurs when an operation on unsigned integer variables can result in values that cannot be represented by the result data type. The data type of a variable determines the number of bytes allocated for the variable storage and constrains the range of allowed values.

The exact storage allocation for different floating point types depends on your processor. See Target processor type (-target).

Risk

The C11 standard states that unsigned integer overflows result in wrap-around behavior. However, a wrap around behavior might not always be desirable. For instance, if the result of a computation is used as an array size and the computation overflows, the array size is much smaller than expected.

Fix

The fix depends on the root cause of the defect. Often the result details (or source code tooltips in Polyspace as You Code) show a sequence of events that led to the defect. You can implement the fix on any event in the sequence. If the result details do not show this event history, you can search for previous references of variables relevant to the defect using right-click options in the source code and find related events. See also Interpret Bug Finder Results in Polyspace Desktop User Interface or Interpret Bug Finder Results in Polyspace Access Web Interface (Polyspace Access).

You can fix the defect by:

  • Using a bigger data type for the result of the operation so that all values can be accommodated.

  • Checking for values that lead to the overflow and performing appropriate error handling. In the error handling code, you can override the default wrap-around behavior for overflows and implement saturation behavior, for instance.

See examples of fixes below.

If you do not want to fix the issue, add comments to your result or code to avoid another review. See:

Extend Checker

A default Bug Finder analysis might not raise this defect when the input values are unknown and only a subset of inputs cause an issue. To check for defects caused by specific system input values, run a stricter Bug Finder analysis. See Extend Bug Finder Checkers to Find Defects from Specific System Input Values.

Example — Add One to Maximum Unsigned Integer
#include <limits.h>

unsigned int plusplus(void) {

    unsigned uvar = UINT_MAX;
    uvar++;  //Noncompliant
    return uvar;
}

In the third statement of this function, the variable uvar is increased by 1. However, the value of uvar is the maximum unsigned integer value, so 1 plus the maximum integer value cannot be represented by an unsigned int. The C programming language standard does not view unsigned overflow as an error because the program automatically reduces the result by modulo the maximum value plus 1. In this example, uvar is reduced by modulo UINT_MAX. The result is uvar = 1.

Correction — Different Storage Type

One possible correction is to store the operation result in a larger data type. In this example, by returning an unsigned long long instead of an unsigned int, the overflow error is fixed.

#include <limits.h>

unsigned long long plusplus(void) {

    unsigned long long ullvar = UINT_MAX;
    ullvar++;
    return ullvar;
}

Check Information

Category: Numeric Errors

Version History

Introduced in R2023a