Main Content

CWE Rule 682

Incorrect Calculation

Since R2024a

Description

Rule Description

The product performs a calculation that generates incorrect or unintended results that are later used in security-critical decisions or resource management.

Polyspace Implementation

The rule checker checks for these issues:

  • Absorption of float operand

  • Bitwise operation on negative value

  • Float overflow

  • Invalid use of standard library floating point routine

  • Invalid use of standard library integer routine

  • Tainted modulo operand

  • Use of plain char type for numeric value

Examples

expand all

Issue

This issue occurs when one operand of an addition or subtraction operation is always negligibly small compared to the other operand. Therefore, the result of the operation is always equal to the value of the larger operand, making the operation redundant.

Risk

Redundant operations waste execution cycles of your processor.

The absorption of a float operand can indicate design issues elsewhere in the code. It is possible that the developer expected a different range for one of the operands and did not expect the redundancy of the operation. However, the operand range is different from what the developer expects because of issues elsewhere in the code.

Fix

See if the operand ranges are what you expect. To see the ranges, place your cursor on the operation.

  • If the ranges are what you expect, justify why you have the redundant operation in place. For instance, the code is only partially written and you anticipate other values for one or both of the operands from future unwritten code.

    If you cannot justify the redundant operation, remove it.

  • If the ranges are not what you expect, in your code, trace back to see where the ranges come from. To begin your traceback, search for instances of the operand in your code. Browse through previous instances of the operand and determine where the unexpected range originates.

To determine when one operand is negligible compared to the other operand, the defect uses rules based on IEEE® 754 standards. To fix the defect, instead of using the actual rules, you can use this heuristic: the ratio of the larger to the smaller operand must be less than 2p-1 at least for some values. Here, p is equal to 24 for 32-bit precision and 53 for 64-bit precision. To determine the precision, the defect uses your specification for Target processor type (-target).

This defect appears only if one operand is always negligibly smaller than the other operand. To see instances of subnormal operands or results, use the check Subnormal Float in Polyspace® Code Prover™.

Example — One Addition Operand Negligibly Smaller Than The Other Operand
#include <stdlib.h>

float get_signal(void);
void do_operation(float);

float input_signal1(void) {
    float temp = get_signal();
    if(temp > 0. && temp < 1e-30)
        return temp;
    else {
       /* Reject value */    
       exit(EXIT_FAILURE);
    }
}

float input_signal2(void) {
    float temp = get_signal();
    if(temp > 1.)
        return temp;
    else {
       /* Reject value */    
       exit(EXIT_FAILURE);
    }
}

void main() {
    float signal1 = input_signal1();
    float signal2 = input_signal2();
    float super_signal = signal1 + signal2; //Noncompliant
    do_operation(super_signal);
}

In this example, the defect appears on the addition because the operand signal1 is in the range (0,1e-30) but signal2 is greater than 1.

Correction — Remove Redundant Operation

One possible correction is to remove the redundant addition operation. In the following corrected code, the operand signal2 and its associated code is also removed from consideration.

#include <stdlib.h>

float get_signal(void);
void do_operation(float);

float input_signal1(void) {
    float temp = get_signal();
    if(temp > 0. && temp < 1e-30)
        return temp;
    else {
       /* Reject value */    
       exit(EXIT_FAILURE);
    }
}

void main() {
    float signal1 = input_signal1();
    do_operation(signal1);
}
Correction — Verify Operand Range

Another possible correction is to see if the operand ranges are what you expect. For instance, if one of the operand range is not supposed to be negligibly small, fix the issue causing the small range. In the following corrected code, the range (0,1e-2) is imposed on signal2 so that it is not always negligibly small as compared to signal1.

#include <stdlib.h>

float get_signal(void);
void do_operation(float);

float input_signal1(void) {
    float temp = get_signal();
    if(temp > 0. && temp < 1e-2)
        return temp;
    else {
       /* Reject value */    
       exit(EXIT_FAILURE);
    }
}

float input_signal2(void) {
    float temp = get_signal();
    if(temp > 1.)
        return temp;
    else {
       /* Reject value */    
       exit(EXIT_FAILURE);
    }
}

void main() {
    float signal1 = input_signal1();
    float signal2 = input_signal2();
    float super_signal = signal1 + signal2;
    do_operation(super_signal);
}
Issue

This issue occurs when bitwise operators (>>, ^, |, ~, but, not, &) are used on signed integer variables with negative values.

Risk

If the value of the signed integer is negative, bitwise operation results can be unexpected because:

  • Bitwise operations on negative values can produce compiler-specific results.

  • Unexpected calculations can lead to additional vulnerabilities, such as buffer overflow.

Fix

When performing bitwise operations, use unsigned integers to avoid unexpected results.

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 — Right-Shift of Negative Integer
#include <stdio.h>
#include <stdarg.h>

static void demo_sprintf(const char *format, ...)
{
    int rc;
    va_list ap;
    char buf[sizeof("256")];

    va_start(ap, format);
    rc = vsprintf(buf, format, ap);
    if (rc == -1 || rc >= sizeof(buf)) {
        /* Handle error */
    }
    va_end(ap);
}

void bug_bitwiseneg()
{
    int stringify = 0x80000000;
    demo_sprintf("%u", stringify >> 24); //Noncompliant
}

In this example, the statement demo_sprintf("%u", stringify >> 24) stops the program unexpectedly. You expect the result of stringify >> 24 to be 0x80. However, the actual result is 0xffffff80 because stringify is signed and negative. The sign bit is also shifted.

Correction — Add unsigned Keyword

By adding the unsigned keyword, stringify is not negative and the right-shift operation gives the expected result of 0x80.

#include <stdio.h>
#include <stdarg.h>

static void demo_sprintf(const char *format, ...)
{
    int rc;
    va_list ap;
    char buf[sizeof("256")];

    va_start(ap, format);
    rc = vsprintf(buf, format, ap);
    if (rc == -1 || rc >= sizeof(buf)) {
        /* Handle error */
    }
    va_end(ap);
}

void corrected_bitwiseneg()
{
    unsigned int stringify = 0x80000000;
    demo_sprintf("%u", stringify >> 24);
}
Issue

This issue occurs when an operation on floating point 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, Float 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 float 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 types depends on your processor. See Target processor type (-target).

Risk

Overflows can result in unpredictable values from computations. The result can be infinity or the maximum finite value depending on the rounding mode used in the implementation. If you use the result of an overflowing computation in subsequent computations and do not account for the overflow, you can see unexpected results.

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).

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:

By default, a Bug Finder analysis does not recognize infinities and NaNs. Operations that results in infinities and NaNs might be flagged as defects. To handle infinities and NaN values in your code, use the option Consider non finite floats (-allow-non-finite-floats)..

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 — Multiplication of Floats
#include <float.h>

float square(void) {

   float val = FLT_MAX;
   return val * val;   //Noncompliant
}

In the return statement, the variable val is multiplied by itself. The square of the maximum float value cannot be represented by a float (the return type for this function) because the value of val is the maximum float value.

Correction — Different Storage Type

One possible correction is to store the result of the operation in a larger data type. In this example, by returning a double instead of a float, the overflow defect is fixed.

#include <float.h>

double square(void) {
    float val = FLT_MAX;

    return (double)val * (double)val;  
}
Issue

This issue occurs when you use invalid arguments with a floating point function from the standard library and the standard name space. This defect picks up:

  • Rounding and absolute value functions (ceil(), fabs(), floor(), fmod(), and so on)

  • Fractions and division functions (fmod(), modf()

  • Exponents and log functions (frexp(), ldexp(), sqrt(), pow(), exp(), log(), log10(), and so on)

  • Trigonometry functions (cos(), sin(), tan(), acos(), asin(), atan(), atan2(), and so on)

Risk

Domain errors on standard library floating point functions result in implementation-defined values. If you use the function return value in subsequent computations, you can see unexpected results.

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).

It is a good practice to handle for domain errors before using a standard library floating point function. For instance, before calling the acos function, check if the argument is in [-1.0, 1.0] and handle the error.

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:

By default, a Bug Finder analysis does not recognize infinities and NaNs. Operations that results in infinities and NaNs might be flagged as defects. To handle infinities and NaN values in your code, use the option Consider non finite floats (-allow-non-finite-floats).

Extend Checker

Extend this checker to check for defects caused by specific values and invalid use of functions from a custom library. For instance:

Example — Arc Cosine Operation
#include <math.h>

double arccosine(void) {
    double degree = 5.0;
    return acos(degree); //Noncompliant
}

The input value to acos must be in the interval [-1,1]. This input argument, degree, is outside this range.

Correction — Change Input Argument

One possible correction is to change the input value to fit the specified range. In this example, change the input value from degrees to radians to fix this defect.

#include <math.h>

double arccosine(void) {
    double degree = 5.0;
    double radian = degree * 3.14159 / 180.;
    return acos(radian);
}
Example — Invalid Use of Functions in Standard Namespace
#include <cmath>

void InvalidUse(double& val1, double& val2){
  int in = -5;
  int ratio = 0;
  //...
  val1 = std::sqrt(in); //Noncompliant
  val2 = std::log(ratio); //Noncompliant
}

The input value to std::sqrt() must be nonnegative. Polyspace flags the use of negative input argument in. The input value to std::log must be greater than zero. Polyspace flags calling std::log by using ratio.

Correction — Refactor Code

One possible correction is to check the logic and refactor your code. For instance, instead of taking the square root of -5, perhaps val1 is indented to be the negative square root of 5. The function might be defining ratio incorrectly as well.

#include <cmath>
int getRatio(void);
void ValidUse(double& val1, double& val2){
  int in = 5;
  int ratio = getRatio();
  //...
  val1 = -1* std::sqrt(in);
  val2 = std::log(ratio);
}
Issue

This issue occurs when you use invalid arguments with an integer function from the standard library. This defect picks up:

  • Character conversion functions (toupper(), tolower(), and so on)

  • Character check functions (isalnum(), isalpha(), iscntrl(), isdigit, and so on)

  • Integer division functions (div(), ldiv(), and so on)

  • Absolute value functions (abs(), labs(), and so on)

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).

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

Extend this checker to check for defects caused by specific values and invalid use of functions from a custom library. For instance:

Example — Absolute Value of Large Negative
#include <limits.h>
#include <stdlib.h>

int absoluteValue(void) {

    int neg = INT_MIN;
    return abs(neg); //Noncompliant
}

The input value to abs is INT_MIN. The absolute value of INT_MIN is INT_MAX+1. This number cannot be represented by the type int.

Correction — Change Input Argument

One possible correction is to change the input value to fit returned data type. In this example, change the input value to INT_MIN+1.

#include <limits.h>
#include <stdlib.h>

int absoluteValue(void) {

    int neg = INT_MIN+1;
    return abs(neg);
}
Issue

This issue occurs when one or both integer operands in a remainder operation (%) comes from unsecure sources.

Risk

  • If the second remainder operand is zero, your remainder operation fails, causing your program to crash.

  • If the second remainder operand is -1, your remainder operation can overflow if the remainder operation is implemented based on the division operation that can overflow.

  • If one of the operands is negative, the operation result is uncertain. For C89, the modulo operation is not standardized, so the result from negative operands is implementation-defined.

These risks can be exploited by attackers to gain access to your program or the target in general.

Fix

Before performing the modulo operation, validate the values of the operands. Check the second operand for values of 0 and -1. Check both operands for negative values.

Extend Checker

By default, Polyspace assumes that data from external sources are tainted. See Sources of Tainting in a Polyspace Analysis. To consider any data that does not originate in the current scope of Polyspace analysis as tainted, use the command line option -consider-analysis-perimeter-as-trust-boundary.

Example — Modulo with User Input
#include <stdio.h>
extern void print_int(int);

int taintedintmod(void) {
    int userden;
    scanf("%d", &userden);
    int rem =  128%userden; //Noncompliant //Noncompliant
    print_int(rem);
    return rem;
}

In this example, the function performs a modulo operation by using a user input. The input is not checked before calculating the remainder for values that can crash the program, such as 0 and -1.

Correction — Check Operand Values

One possible correction is to check the values of the operands before performing the modulo operation. In this corrected example, the modulo operation continues only if the second operand is greater than zero.

#include<stdio.h>
extern void print_int(int);

int taintedintmod(void) {
    int userden;
    scanf("%d", &userden);
    int rem = 0;
    if (userden > 0 ) { 
        rem = 128 % userden; 
    }
    print_int(rem);
    return rem;
}
Issue

This issue occurs when char variables without explicit signedness are used in these ways:

  • To store non-char constants.

  • In an arithmetic operation when the char is:

    • A negative value.

    • The result of a sign changing overflow.

  • As a buffer offset.

char variables without a signed or unsigned qualifier can be signed or unsigned depending on your compiler.

Risk

Operations on a plain char can result in unexpected numerical values. If the char is used as an offset, the char can cause buffer overflow or underflow.

Fix

When initializing a char variable, to avoid implementation-defined confusion, explicitly state whether the char is signed or unsigned.

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 — Divide by char Variable
#include <stdio.h>

void badplaincharuse(void)
{
    char c = 200;
    int i = 1000;
    (void)printf("i/c = %d\n", i/c); //Noncompliant
}

In this example, the char variable c can be signed or unsigned depending on your compiler. Assuming 8-bit, two's complement character types, the result is either i/c = 5 (unsigned char) or i/c = -17 (signed char). The correct result is unknown without knowing the signedness of char.

Correction — Add signed Qualifier

One possible correction is to add a signed qualifier to char. This clarification makes the operation defined.

#include <stdio.h>

void badplaincharuse(void)
{
    signed char c = -56;
    int i = 1000;
    (void)printf("i/c = %d\n", i/c);
}

Check Information

Category: Others

Version History

Introduced in R2024a