Main Content

CWE Rule 704

Incorrect Type Conversion or Cast

Since R2023a

Description

Rule Description

The software does not correctly convert an object, resource, or structure from one type to a different type.

Polyspace Implementation

The rule checker checks for these issues:

  • Cast to pointer pointing to object of different type

  • Character value absorbed into EOF

  • Conversion between pointer and integer

  • Integer conversion overflow

  • Misuse of sign-extended character value

  • Precision loss in integer to float conversion

  • Qualifier removed in conversion

  • Reading memory reallocated from object of another type without reinitializing first

  • Sign change integer conversion overflow

  • Tainted sign change conversion

  • Unreliable cast of pointer

  • Unsafe conversion between pointer and integer

  • Unsigned integer conversion overflow

  • Wrong allocated object size for cast

Examples

expand all

Issue

The issue occurs when you perform a cast between a pointer to an object type and a pointer to a different object type.

Risk

If a pointer to an object is cast into a pointer to a different object, the resulting pointer can be incorrectly aligned. The incorrect alignment causes undefined behavior.

Even if the conversion produces a pointer that is correctly aligned, the behavior can be undefined if the pointer is used to access an object.

Exception: You can convert a pointer to object type into a pointer to one of these types:

  • char

  • signed char

  • unsigned char

Example - Cast to Pointer Pointing to Object of Wider Type
signed   char *p1;
unsigned int *p2;

void foo(void){ 
  p2 = ( unsigned int * ) p1;     /* Non-compliant */				
}

In this example, p1 can point to a signed char object. However, p1 is cast to a pointer that points to an object of wider type, unsigned int.

Example - Cast to Pointer Pointing to Object of Narrower Type
extern unsigned int read_value ( void );
extern void display ( unsigned int n );

void foo ( void ){
  unsigned int u = read_value ( );
  unsigned short *hi_p = ( unsigned short * ) &u;    /* Non-compliant  */	
  *hi_p = 0;                                         
  display ( u );                                     
}

In this example, u is an unsigned int variable. &u is cast to a pointer that points to an object of narrower type, unsigned short.

On a big-endian machine, the statement *hi_p = 0 attempts to clear the high bits of the memory location that &u points to. But, from the result of display(u), you might find that the high bits have not been cleared.

Example - Cast Adding a Type Qualifier
const short *p;
const volatile short *q;
void foo (void){
  q = ( const volatile short * ) p;  /* Compliant */								
}

In this example, both p and q can point to short objects. The cast between them adds a volatile qualifier only and is therefore compliant.

Issue

This issue occurs when you perform a data type conversion that makes a valid character value indistinguishable from EOF (End-of-File). Bug Finder flags the defect in one of the following situations:

  • End-of-File: You perform a data type conversion such as from int to char that converts a non-EOF character value into EOF.

    char ch = (char)getchar()
    You then compare the result with EOF.
    if((int)ch == EOF)
    The conversion can be explicit or implicit.

  • Wide End-of-File: You perform a data type conversion that can convert a non-WEOF wide character value into WEOF, and then compare the result with WEOF.

Risk

The data type char cannot hold the value EOF that indicates the end of a file. Functions such as getchar have return type int to accommodate EOF. If you convert from int to char, the values UCHAR_MAX (a valid character value) and EOF get converted to the same value -1 and become indistinguishable from each other. When you compare the result of this conversion with EOF, the comparison can lead to false detection of EOF. This rationale also applies to wide character values and WEOF.

Fix

Perform the comparison with EOF or WEOF before conversion.

Example — Return Value of getchar Converted to char
#include <stdio.h>
#include <stdlib.h>
#define fatal_error() abort()

char func(void)
{
    char ch;
    ch = getchar();
    if (EOF == (int)ch) {  //Noncompliant
        fatal_error();
    }
    return ch;
}

In this example, the return value of getchar is implicitly converted to char. If getchar returns UCHAR_MAX, it is converted to -1, which is indistinguishable from EOF. When you compare with EOF later, it can lead to a false positive.

Correction — Perform Comparison with EOF Before Conversion

One possible correction is to first perform the comparison with EOF, and then convert from int to char.

#include <stdio.h>
#include <stdlib.h>
#define fatal_error() abort()

char func(void)
{
    int i;               
    i = getchar();       
    if (EOF == i) {      
        fatal_error();
    }
    else {
        return (char)i;
    }
}
Issue

The issue occurs when you convert an integral type into a raw pointer type. Polyspace® reports the conversion as a violation of the rule regardless of the sizes of the integral and raw pointer types. For instance, if an integer originates from a pointer and then later, is cast into a pointer, Polyspace flags the conversion from integer to pointer. In this case, the integer and the pointer have the same size.

Risk

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

Converting from integers to pointers can create:

  • Misaligned pointers or misaligned objects.

  • Invalid pointer addresses.

Fix

Where possible, avoid 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)
{
    int* ptr;
    unsigned long long int_same_as_ptr ;
    unsigned int int_smaller_than_ptr;

    unsigned int* ptr0 = (unsigned int*)0xdeadbeef; //Noncompliant
    

    /*int to ptr of same size*/
    ptr = (int*)int_same_as_ptr;  //Noncompliant
    
    /*int to ptr of different size*/
    ptr = (int*)int_smaller_than_ptr;  //Noncompliant

    return (unsigned int*)(ptr0 - (unsigned int*)ptr);  //Noncompliant
}

In this example, Polyspace flags the conversions that might be unsafe. For instance:

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

  • The conversion of the int_same_as_ptr and int_smaller_than_ptr to int* pointers might result in invalid pointer address. Polyspace flags these conversion.

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

Correction — Use intptr_t

One possible correction is to use intptr_t types to store addresses. When storing the result of subtracting two pointers, use the type ptrdiff_t.

#include<stdint.h>
#include<stddef.h>
ptrdiff_t badintptrcast(void)
{
    intptr_t ptr;
    unsigned long long int_same_as_ptr ;
    unsigned int int_smaller_than_ptr;

    intptr_t ptr0 = (intptr_t)0xdeadbeef; //Compliant

    /*int to ptr of same size*/
    ptr = (intptr_t)int_same_as_ptr;  //Compliant
    
    /*int to ptr of different size*/
    ptr = (intptr_t)int_smaller_than_ptr;  //Compliant

    return (ptrdiff_t)ptr0;//Compliant
    
}
Issue

This issue occurs when converting an integer to a smaller integer type. If the variable does not have enough bytes to represent the original value, the conversion overflows. For instance, if you perform a comparison between implementation-defined type time_t and a signed integer, Polyspace reports a violation because time_t might be implemented as an unsigned integer.

The checker also detects comparison

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
char convert(void) {

    int num = 1000000;

    return (char)num;  //Noncompliant
}

In the return statement, the integer variable num is converted to a char. However, an 8-bit or 16-bit character cannot represent 1000000 because it requires at least 20 bits. So the conversion operation overflows.

Correction — Change Conversion Type

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

long convert(void) {

    int num = 1000000;

    return (long)num;
}
Issue

This issue occurs when you convert a signed or plain char variable containing possible negative values to a wider integer data type (or perform an arithmetic operation that does the conversion) and then use the resulting value in one of these ways:

  • For comparison with EOF (using == or !=)

  • As array index

  • As argument to a character-handling function in ctype.h, for instance, isalpha() or isdigit()

If you convert a signed char variable with a negative value to a wider type such as int, the sign bit is preserved (sign extension). This can lead to specific problems even in situations where you think you have accounted for the sign bit.

For instance, the signed char value of -1 can represent the character EOF (end-of-file), which is an invalid character. Suppose a char variable var acquires this value. If you treat var as a char variable, you might want to write special code to account for this invalid character value. However, if you perform an operation such as var++ (involving integer promotion), it leads to the value 0, which represents a valid value '\0' by accident. You transitioned from an invalid to a valid value through the arithmetic operation.

Even for negative values other than -1, a conversion from signed char to signed int can lead to other issues. For instance, the signed char value -126 is equivalent to the unsigned char value 130 (corresponding to an extended character '\202'). If you convert the value from char to int, the sign bit is preserved. If you then cast the resulting value to unsigned int, you get an unexpectedly large value, 4294967170 (assuming 32-bit int). If your code expects the unsigned char value of 130 in the final unsigned int variable, you can see unexpected results.

The underlying cause of this issue is the sign extension during conversion to a wider type. Most architectures use two's complement representation for storing values. In this representation, the most significant bit indicates the sign of the value. When converted to a wider type, the conversion is done by copying this sign bit to all the leading bits of the wider type, so that the sign is preserved. For instance, the char value of -3 is represented as 11111101 (assuming 8-bit char). When converted to int, the representation is:

11111111 11111111 11111111  11111101
The value -3 is preserved in the wider type int. However, when converted to unsigned int, the value (4294967293) is no longer the same as the unsigned char equivalent of the original char value. If you are not aware of this issue, you can see unexpected results in your code.

Risk

In the following cases, Bug Finder flags use of variables after a conversion from char to a wider data type or an arithmetic operation that implicitly converts the variable to a wider data type:

  • If you compare the variable value with EOF:

    A char value of -1 can represent the invalid character EOF or the valid extended character value '\377' (corresponding to the unsigned char equivalent, 255). After a char variable is cast to a wider type such as int, because of sign extension, the char value -1, representing one of EOF or '\377' becomes the int value -1, representing only EOF. The unsigned char value 255 can no longer be recovered from the int variable. Bug Finder flags this situation so that you can cast the variable to unsigned char first (or avoid the char-to-int conversion or converting operation before comparison with EOF). Only then, a comparison with EOF is meaningful. See Sign-Extended Character Value Compared with EOF.

  • If you use the variable value as an array index:

    After a char variable is cast to a wider type such as int, because of sign extension, all negative values retain their sign. If you use the negative values directly to access an array, you cause buffer overflow/underflow. Even when you account for the negative values, the way you account for them might result in incorrect elements being read from the array. See Sign-Extended Character Value Used as Array Index.

  • If you pass the variable value as argument to a character-handling function:

    According to the C11 standard (Section 7.4), if you supply an integer argument that cannot be represented as unsigned char or EOF, the resulting behavior is undefined. Bug Finder flags this situation because negative char values after conversion can no longer be represented as unsigned char or EOF. For instance, the signed char value -126 is equivalent to the unsigned char value 130, but the signed int value -126 cannot be represented as unsigned char or EOF.

Fix

Before conversion to a wider integer data type, cast the signed or plain char value explicitly to unsigned char.

If you use the char data type to not represent characters but simply as a smaller data type to save memory, your use of sign-extended char values might avoid the risks mentioned earlier. If so, add comments to your result or code to avoid another review. See:

Example — Sign-Extended Character Value Compared with EOF
#include <stdio.h>
#include <stdlib.h>
#define fatal_error() abort()

extern char parsed_token_buffer[20];

static int parser(char *buf)
{
    int c = EOF;
    if (buf && *buf) {
        c = *buf++;    
    }
    return c;
}

void func()
{
    if (parser(parsed_token_buffer) == EOF) { //Noncompliant
        /* Handle error */
        fatal_error();
    }
}

In this example, the function parser can traverse a string input buf. If a character in the string has the value -1, it can represent either EOF or the valid character value '\377' (corresponding to the unsigned char equivalent 255). When converted to the int variable c, its value becomes the integer value -1, which is always EOF. The later comparison with EOF will not detect if the value returned from parser is actually EOF.

Correction — Cast to unsigned char Before Conversion

One possible correction is to cast the plain char value to unsigned char before conversion to the wider int type. Only then can you test if the return value of parser is really EOF.

#include <stdio.h>
#include <stdlib.h>
#define fatal_error() abort()

extern char parsed_token_buffer[20];

static int parser(char *buf)
{
    int c = EOF;
    if (buf && *buf) {
        c = (unsigned char)*buf++;    
    }
    return c;
}

void func()
{
    if (parser(parsed_token_buffer) == EOF) { 
        /* Handle error */
        fatal_error();
    }
}
Example — Sign-Extended Character Value Used as Array Index
#include <limits.h>
#include <stddef.h>
#include <stdio.h>

#define NUL '\0'
#define SOH 1    /* start of heading */
#define STX 2    /* start of text */
#define ETX 3    /* end of text */
#define EOT 4    /* end of transmission */
#define ENQ 5    /* enquiry */
#define ACK 6    /* acknowledge */

static const int ascii_table[UCHAR_MAX + 1] =
{
      [0]=NUL,[1]=SOH, [2]=STX, [3]=ETX, [4]=EOT, [5]=ENQ,[6]=ACK,
      /* ... */
      [126] = '~',
      /* ... */
      [130/*-126*/]='\202',
      /* ... */
      [255 /*-1*/]='\377'
};

int lookup_ascii_table(char c)
{
    int i;
    i = (c < 0 ? -c : c);
    return ascii_table[i];  //Noncompliant
}

In this example, the char variable c is converted to the int variable i. If c has negative values, they are converted to positive values before assignment to i. However, this conversion can lead to unexpected values when i is used as array index. For instance:

  • If c has the value -1 representing the invalid character EOF, you want to probably treat this value separately. However, in this example, a value of c equal to -1 leads to a value of i equal to 1. The function lookup_ascii_table returns the value ascii_table[1] (or SOH) without the invalid character value EOF being accounted for.

    If you use the char data type to not represent characters but simply as a smaller data type to save memory, you need not worry about this issue.

  • If c has a negative value, when assigned to i, its sign is reversed. However, if you access the elements of ascii_table through i, this sign reversal can result in unexpected values being read.

    For instance, if c has the value -126, i has the value 126. The function lookup_ascii_table returns the value ascii_table[126] (or '~') but you probably expected the value ascii_table[130] (or '\202').

Note that the example uses designated initializers for array initialization and does not compile in C++. You can adapt the example appropriately for C++ compilation.

Correction – Cast to unsigned char

To correct the issues, avoid the conversion from char to int. First, check c for the value EOF. Then, cast the value of the char variable c to unsigned char and use the result as array index.

#include <limits.h>
#include <stddef.h>
#include <stdio.h>

#define NUL '\0'
#define SOH 1    /* start of heading */
#define STX 2    /* start of text */
#define ETX 3    /* end of text */
#define EOT 4    /* end of transmission */
#define ENQ 5    /* enquiry */
#define ACK 6    /* acknowledge */

static const int ascii_table[UCHAR_MAX + 1] =
{
      [0]=NUL,[1]=SOH, [2]=STX, [3]=ETX, [4]=EOT, [5]=ENQ,[6]=ACK,
      /* ... */
      [126] = '~',
      /* ... */
      [130/*-126*/]='\202',
      /* ... */
      [255 /*-1*/]='\377'
};

int lookup_ascii_table(char c)
{
    int r = EOF;
    if (c != EOF) /* specific handling EOF, invalid character */
        r = ascii_table[(unsigned char)c]; /* cast to 'unsigned char' */
    return r;
}
Issue

This issue occurs when you cast an integer value to a floating-point type that cannot represent the original integer value.

For instance, the long int value 1234567890L is too large for a variable of type float.

Risk

If the floating-point type cannot represent the integer value, the behavior is undefined (see C11 standard, 6.3.1.4, paragraph 2). For instance, least significant bits of the variable value can be dropped leading to unexpected results.

Fix

Convert to a floating-point type that can represent the integer value.

For instance, if the float data type cannot represent the integer value, use the double data type instead.

When writing a function that converts an integer to floating point type, before the conversion, check if the integer value can be represented in the floating-point type. For instance, DBL_MANT_DIG * log2(FLT_RADIX) represents the number of base-2 digits in the type double. Before conversion to the type double, check if this number is greater than or equal to the precision of the integer that you are converting. To determine the precision of an integer num, use this code:

 size_t precision = 0;
 while (num != 0) {
    if (num % 2 == 1) {
      precision++;
    }
    num >>= 1;
 }

Some implementations provide a builtin function to determine the precision of an integer. For instance, GCC provides the function __builtin_popcount.

Example — Conversion of Large Integer to Floating-Point Type
#include <stdio.h>

int main(void) {
  long int big = 1234567890L;
  float approx = big;  //Noncompliant
  printf("%ld\n", (big - (long int)approx));
  return 0;
}

In this C code, the long int variable big is converted to float.

Correction — Use a Wider Floating-Point Type

One possible correction is to convert to the double data type instead of float.

#include <stdio.h>

int main(void) {
  long int big = 1234567890L;
  double approx = big;
  printf("%ld\n", (big - (long int)approx));
  return 0;
}
Issue

This checker is deactivated in a default Polyspace as You Code analysis. See Checkers Deactivated in Polyspace as You Code Analysis (Polyspace Access).

This issue occurs during a pointer conversion when one pointer has a qualifier and the other does not. For example, when converting from a const int* to an int*, the conversion removes the const qualifier.

This defect applies only for projects in C.

Risk

Qualifiers such as const or volatile in a pointer declaration:

const int* ptr;
imply that the underlying object is const or volatile. These qualifiers act as instructions to the compiler. For instance, a const object is not supposed to be modified in the code and a volatile object is not supposed to be optimized away by the compiler.

If a second pointer points to the same object but does not use the same qualifier, the qualifier on the first pointer is no longer valid. For instance, if a const int* pointer and an int* pointer point to the same object, you can modify the object through the second pointer and violate the contract implied by the const qualifier in the first pointer.

Fix

If you intend to convert from one pointer to another, declare both pointers with the same qualifiers.

Example — Cast of Character Pointers
void implicit_cast(void) {
    const char  cc, *pcc = &cc;
    char * quo;

    quo = &cc;  //Noncompliant
    quo = pcc;  //Noncompliant

    read(quo);
}

During the assignment to the character q, the variables, cc and pcc, are converted from const char to char. The const qualifier is removed during the conversion causing a defect.

Correction — Add Qualifiers

One possible correction is to add the same qualifiers to the new variables. In this example, changing q to a const char fixes the defect.

void implicit_cast(void) {
    const char  cc, *pcc = &cc;
    const char * quo;

    quo = &cc;
    quo = pcc;

    read(quo);
}
Correction — Remove Qualifiers

One possible correction is to remove the qualifiers in the converted variable. In this example, removing the const qualifier from the cc and pcc initialization fixes the defect.

void implicit_basic_cast(void) {
    char  cc, *pcc = &cc;
    char * quo;

    quo = &cc;
    quo = pcc;

    read(quo);
}
Issue

This issue occurs when you do the following in sequence:

  1. Reallocate memory to an object with a type that is different from the original allocation.

    For instance, in this code snippet, a memory originally allocated to a pointer with type struct A* is reallocated to a pointer with type struct B*:

    struct A;
    struct B;
    
    struct A *Aptr = (struct A*) malloc(sizeof(struct A));
    struct B *Bptr = (struct B*) realloc(Aptr, sizeof(struct B));

  2. Read from this reallocated memory without reinitializing the memory first.

    Read accesses on the pointer to the reallocated memory can happen through pointer dereference or array indexing. Passing the pointer to a function that takes a pointer to a const-qualified object as the corresponding parameter also counts as a read access.

Risk

Reading from reallocated memory that has not been reinitialized leads to undefined behavior.

Fix

Reinitialize memory after reallocation and before the first read access.

The checker considers any write access on the pointer to the reallocated memory as satisfying the reinitialization requirement (even if the object might only be partially reinitialized). Write accesses on the pointer to the reallocated memory can happen through pointer dereference or array indexing. Passing the pointer to a function that takes a pointer to a non-const-qualified object as the corresponding parameter also counts as a write access.

Example – Noncompliant: Reading from Reallocated Memory Without Reinitializing First
#include<stdlib.h>

struct group {
    char *groupFirst;
    int groupSize;
};

struct groupWithID {
    int groupID;
    char *groupFirst;
    int groupSize;
};

char* readName();
int readSize();

void createGroup(int nextAvailableID) {
    struct group *aGroup;
    struct groupWithID *aGroupWithID;
    
    aGroup = (struct group*) malloc(sizeof(struct group));
    
    if(!aGroup) {
        /*Handle error*/
    }
    
    aGroup->groupFirst = readName();
    aGroup->groupSize  = readSize();
    
    /* Reassign to group with ID */
    aGroupWithID = (struct groupWithID*) realloc(aGroup, sizeof(struct groupWithID)); //Noncompliant
    if(!aGroupWithID) {
        free(aGroup);
        /*Handle error*/
    }
    
    if(aGroupWithID -> groupSize > 0) { /* Noncompliant */
        /* ... */
    }
    
    /* ...*/
    free(aGroupWithID);
}

In this example, the memory allocated to a group* pointer using the malloc function is reallocated to a groupWithID* pointer using the realloc function. There is a read access on the reallocated memory before the memory is reinitialized, causing a violation. The reallocation operation allocates memory the size of which is not a multiple of the destination type, causing a violation because of the issue Wrong allocated object size for cast.

Correction – Reinitialize Memory After Reallocation and Before First Read

Reinitialize the memory assigned to the groupWithID* pointer before the first read access. All bits of the memory can be reinitialized using the memset function.

#include<stdlib.h>
#include<string.h>

struct group {
    char *groupFirst;
    int groupSize;
};

struct groupWithID {
    int groupID;
    char *groupFirst;
    int groupSize;
};

char* readName();
int readSize();

void createGroup(int nextAvailableID) {
    struct group *aGroup;
    struct groupWithID *aGroupWithID;
    
    aGroup = (struct group*) malloc(sizeof(struct group));
    
    if(!aGroup) {
        /*Handle error*/
    }
    
    aGroup->groupFirst = readName();
    aGroup->groupSize  = readSize();
    
    /* Reassign to group with ID */
    aGroupWithID = (struct groupWithID*) realloc(aGroup, sizeof(struct groupWithID)); //Noncompliant
    if(!aGroupWithID) {
        free(aGroup);
        /*Handle error*/
    }
    
    memset(aGroupWithID, 0 , sizeof(struct groupWithID));
    /* Reinitialize group */
    if(aGroupWithID -> groupSize > 0) { 
        /* ... */
    }
    
    /* ...*/
    free(aGroupWithID);
}

The violation because of the issue Wrong allocated object size for cast remains in this corrected code.

Issue

This issue occurs when converting an unsigned integer to a signed integer. If the variable does not have enough bytes to represent both the original constant and the sign bit, the conversion overflows.

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

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

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 — Convert from unsigned char to char
char sign_change(void) {
    unsigned char count = 255;

    return (char)count;  //Noncompliant
}

In the return statement, the unsigned character variable count is converted to a signed character. However, char has 8 bits, 1 for the sign of the constant and 7 to represent the number. The conversion operation overflows because 255 uses 8 bits.

Correction — Change conversion types

One possible correction is using a larger integer type. By using an int, there are enough bits to represent the sign and the number value.

int sign_change(void) {
    unsigned char count = 255;

    return (int)count;
}
Issue

This issue occurs when values from unsecure sources are converted, implicitly or explicitly, from signed to unsigned values.

For example, functions that use size_t as arguments implicitly convert the argument to an unsigned integer. Some functions that implicitly convert size_t are:

bcmp
memcpy
memmove
strncmp
strncpy
calloc
malloc
memalign

Risk

If you convert a small negative number to unsigned, the result is a large positive number. The large positive number can create security vulnerabilities. For example, if you use the unsigned value in:

  • Memory size routines — causes allocating memory issues.

  • String manipulation routines — causes buffer overflow.

  • Loop boundaries — causes infinite loops.

Fix

To avoid converting unsigned negative values, check that the value being converted is within an acceptable range. For example, if the value represents a size, validate that the value is not negative and less than the maximum value size.

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 — Set Memory Value with Size Argument
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};

void bug_taintedsignchange(void) {
    int size;
    scanf("%d",&size);
    char str[SIZE128] = "";
    if (size<SIZE128) {
        memset(str, 'c', size); //Noncompliant
    }
}

In this example, a char buffer is created and filled using memset. The size argument to memset is an input argument to the function.

The call to memset implicitly converts size to unsigned integer. If size is a large negative number, the absolute value could be too large to represent as an integer, causing a buffer overflow.

Correction — Check Value of size

One possible correction is to check if size is inside the valid range. This correction checks if size is greater than zero and less than the buffer size before calling memset.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};

void corrected_taintedsignchange(void) {
    int size;
    scanf("%d",&size);
    char str[SIZE128] = "";
    if (size>0 && size<SIZE128) {
        memset(str, 'c', size);  
    }
}
Issue

This issue occurs when a pointer is implicitly cast to a data type different from its declaration type. Such an implicit casting can take place, for instance, when a pointer to data type char is assigned the address of an integer.

This defect applies only if the code language for the project is C.

Risk

Casting a pointer to data type different from its declaration type can result in issues such as buffer overflow. If the cast is implicit, it can indicate a coding error.

Fix

Avoid implicit cast of a pointer to a data type different from its declaration type.

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:

Example — Unreliable cast of pointer error
 #include <string.h>
 
 void Copy_Integer_To_String()
 {
  int src[]={1,2,3,4,5,6,7,8,9,10};
  char buffer[]="Buffer_Text";
  strcpy(buffer,src);  //Noncompliant
  /* Defect: Implicit cast of (int*) to (char*) */
 }

src is declared as an int* pointer. The strcpy statement, while copying to buffer, implicitly casts src to char*.

Correction — Avoid Pointer Cast

One possible correction is to declare the pointer src with the same data type as buffer.

 #include <string.h>
  void Copy_Integer_To_String()
 {
  /* Fix: Declare src with same type as buffer */
  char *src[10]={"1","2","3","4","5","6","7","8","9","10"};  
  char *buffer[10];

  for(int i=0;i<10;i++)
    buffer[i]="Buffer_Text";

  for(int i=0;i<10;i++)
    buffer[i]= src[i];
  }
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.

#include <stdint.h>

unsigned int *badintptrcast(void)
{
    intptr_t iptr0 = (intptr_t)0xdeadbeef;
    return (unsigned int *)iptr0;
}
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 during pointer conversion when the pointer’s address is misaligned. If a pointer is converted to a different pointer type, the size of the allocated memory must be a multiple of the size of the destination pointer.

Risk

Dereferencing a misaligned pointer has undefined behavior and can cause your program to crash.

Fix

Suppose you convert a pointer ptr1 to ptr2. If ptr1 points to a buffer of N bytes and ptr2 is a type * pointer where sizeof(type) is n bytes, make sure that N is an integer multiple of n.

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:

Example — Dynamic Allocation of Pointers
#include <stdlib.h>

void dyn_non_align(void){
    void *ptr = malloc(13);
    long *dest;

    dest = (long*)ptr; //Noncompliant
}

In this example, the software raises a defect on the conversion of ptr to a long*. The dynamically allocated memory of ptr, 13 bytes, is not a multiple of the size of dest, 4 bytes. This misalignment causes the Wrong allocated object size for cast defect.

Correction — Change the Size of the Pointer

One possible correction is to use a pointer size that is a multiple of the destination size. In this example, resolve the defect by changing the allocated memory to 12 instead of 13.

#include <stdlib.h>

void dyn_non_align(void){
    void *ptr = malloc(12);
    long *dest;

    dest = (long*)ptr;
}
Example — Allocation with a Function
#include <stdlib.h>

void *my_alloc(unsigned int size) { 
    void *ptr_func = malloc(size); 
    if(ptr_func == NULL) exit(-1); 
    return ptr_func; 
}

void fun_non_align(void){
    int *dest1;
    char *dest2;

    dest1 = (int*)my_alloc(13);  //Noncompliant
    dest2 = (char*)my_alloc(13); //Compliant
}

In this example, the software raises a defect on the conversion of the pointer returned by my_alloc(13) to an int* in line 11. my_alloc(13) returns a pointer with a dynamically allocated size of 13 bytes. The size of dest1 is 4 bytes, which is not a divisor of 13. This misalignment causes the Wrong allocated object size for cast defect. In line 12, the same function call, my_alloc(13), does not call a defect for the conversion to dest2 because the size of char*, 1 byte, a divisor of 13.

Correction — Change the Size of the Pointer

One possible correction is to use a pointer size that is a multiple of the destination size. In this example, resolve the defect by changing the argument for my_alloc to a multiple of 4.

#include <stdlib.h>

void *my_alloc(unsigned int size) { 
    void *ptr_func = malloc(size); 
    if(ptr_func == NULL) exit(-1); 
    return ptr_func; 
}

void fun_non_align(void){
    int *dest1;
    char *dest2;

    dest1 = (int*)my_alloc(12); 
    dest2 = (char*)my_alloc(13); 
}

Check Information

Category: Others

Version History

Introduced in R2023a

expand all