Main Content

CWE Rule 758

Reliance on Undefined, Unspecified, or Implementation-Defined Behavior

Since R2024a

Description

Rule Description

The product uses an API function, data structure, or other entity in a way that relies on properties that are not always guaranteed to hold for that entity.

Polyspace Implementation

The rule checker checks for these issues:

  • Bitwise operation on negative value

  • Unsafe conversion between pointer and integer

  • Use of plain char type for numeric value

Examples

expand all

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 you convert between a pointer type, such as intptr_t, or uintprt_t, and an integer type, such as enum, ptrdiff_t, or pid_t, or vice versa.

Risk

The mapping between pointers and integers is not always consistent with the addressing structure of the environment.

Converting from pointers to integers can create:

  • Truncated or out of range integer values.

  • Invalid integer types.

Converting from integers to pointers can create:

  • Misaligned pointers or misaligned objects.

  • Invalid pointer addresses.

Fix

Where possible, avoid pointer-to-integer or integer-to-pointer conversions. If you want to convert a void pointer to an integer, so that you do not change the value, use types:

  • C99 — intptr_t or uintptr_t

  • C90 — size_t or ssize_t

Example — Integer to Pointer Conversions
 unsigned int *badintptrcast(void)
{
    unsigned int *ptr0 = (unsigned int *)0xdeadbeef; //Noncompliant
    char *ptr1 = (char *)0xdeadbeef;
    unsigned int *explicit_ptr = reinterpret_cast<unsigned int*>(0xdeadbeef); //Noncompliant
    return (unsigned int *)(ptr0 - (unsigned int *)ptr1); //Noncompliant
}

In this example, there are four conversions, three unsafe conversions and one safe conversion.

  • The conversion of 0xdeadbeef to an unsigned int* causes alignment issues for the pointer. Polyspace® flags this conversion.

  • The conversion of 0xdeadbeef to a char * is not flagged because there are no alignment issues for char.

  • The explicit reinterpret_cast of 0xdeadbeef to an unsigned int* causes alignment issues for the pointer. Polyspace flags this conversion.

  • The conversion in the return casts ptrdiff_t to a pointer. This pointer might point to an invalid address. Polyspace flags this conversion.

Correction — Use intptr_t

One possible correction is to use intptr_t types to store the pointer address 0xdeadbeef. Also, you can change the second pointer to an integer offset so that there is no longer a conversion from ptrdiff_t to a pointer.

#include <stdint.h>

unsigned int *badintptrcast(void)
{
    intptr_t iptr0 = (intptr_t)0xdeadbeef;
    int offset = 0;
    return (unsigned int *)(iptr0 - offset);
}
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