Main Content

CWE Rule 119

Improper Restriction of Operations within the Bounds of a Memory Buffer

Since R2023a

Description

Rule Description

The software performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.

Polyspace Implementation

The rule checker checks for these issues:

  • Array access out of bounds

  • Array access with tainted index

  • Buffer overflow from incorrect string format specifier

  • Cast to pointer pointing to object of different type

  • Destination buffer overflow in string manipulation

  • Destination buffer underflow in string manipulation

  • Invalid use of standard library memory routine

  • Invalid use of standard library routine

  • Invalid use of standard library string routine

  • Mismatch between data length and size

  • Missing null in string array

  • Pointer access out of bounds

  • Pointer dereference with tainted offset

  • Possible misuse of sizeof

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

  • Tainted NULL or non-null-terminated string

  • Use of dangerous standard function

  • Use of indeterminate string

Examples

expand all

Issue

This issue occurs when an array index falls outside the range [0...array_size-1] during array access.

Risk

Accessing an array outside its bounds is undefined behavior. You can read an unpredictable value or try to access a location that is not allowed and encounter a segmentation fault.

Fix

The fix depends on the root cause of the defect. For instance, you accessed an array inside a loop and one of these situations happened:

  • The upper bound of the loop is too large.

  • You used an array index that is the same as the loop index instead of being one less than the loop index.

To fix the issue, you have to modify the loop bound or the array index.

Another reason why an array index can exceed array bounds is a prior conversion from signed to unsigned integers. The conversion can result in a wrap around of the index value, eventually causing the array index to exceed the array bounds.

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 — Array Access Out of Bounds Error
#include <stdio.h>

void fibonacci(void)
{
    int i;
    int fib[10];
 
    for (i = 0; i < 10; i++) 
       {
        if (i < 2) 
            fib[i] = 1;
         else 
            fib[i] = fib[i-1] + fib[i-2];
       }

    printf("The 10-th Fibonacci number is %i .\n", fib[i]);  //Noncompliant
    /* Defect: Value of i is greater than allowed value of 9 */
}

The array fib is assigned a size of 10. An array index for fib has allowed values of [0,1,2,...,9]. The variable i has a value 10 when it comes out of the for-loop. Therefore, the printf statement attempts to access fib[10] through i.

Correction — Keep Array Index Within Array Bounds

One possible correction is to print fib[i-1] instead of fib[i] after the for-loop.

#include <stdio.h>

void fibonacci(void)
{
   int i;
   int fib[10];

   for (i = 0; i < 10; i++) 
    {
        if (i < 2) 
            fib[i] = 1;
        else 
            fib[i] = fib[i-1] + fib[i-2];
    }

    /* Fix: Print fib[9] instead of fib[10] */
    printf("The 10-th Fibonacci number is %i .\n", fib[i-1]); 
}

The printf statement accesses fib[9] instead of fib[10].

Issue

This issue occurs when you access an array by using an index that is obtained from unsecure sources and which has not been validated.

Risk

The index might be outside the valid array range. If the tainted index is outside the array range, it can cause:

  • Buffer underflow/underwrite — writing to memory before the beginning of the buffer.

  • Buffer overflow — writing to memory after the end of a buffer.

  • Over-reading a buffer — accessing memory after the end of the targeted buffer.

  • Under-reading a buffer, or accessing memory before the beginning of the targeted buffer.

An attacker can use an invalid read or write operation create to problems in your program.

Fix

Before using the index to access the array, validate the index value to make sure that it is inside the array range.

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 — Use Index to Return Buffer Value
#include <stdlib.h>
#include <stdio.h>
#define SIZE100 100
extern int tab[SIZE100];
static int tainted_int_source(void) {
  return strtol(getenv("INDEX"),NULL,10);
}
int taintedarrayindex(void) {
	int num = tainted_int_source();
    return tab[num];//Noncompliant   //Noncompliant
}

In this example, the index num accesses the array tab. The index num is obtained from an unsecure source and the function taintedarrayindex does not check to see if num is inside the range of tab.

Correction — Check Range Before Use

One possible correction is to check that num is in range before using it.

#include <stdlib.h>
#include <stdio.h>
#define SIZE100 100
extern int tab[SIZE100];
static int tainted_int_source(void) {
	return strtol(getenv("INDEX"),NULL,10);
}
int taintedarrayindex(void) {
	int num = tainted_int_source();
	if (num >= 0 && num < SIZE100) {
		return tab[num]; 
	} else {
		return -1;
	}
}

Issue

This issue occurs when the format specifier argument for functions such as sscanf leads to an overflow or underflow in the memory buffer argument.

Risk

If the format specifier specifies a precision that is greater than the memory buffer size, an overflow occurs. Overflows can cause unexpected behavior such as memory corruption.

Fix

Use a format specifier that is compatible with the memory buffer size.

Example — Memory Buffer Overflow
#include <stdio.h>

void func (char *str[]) {
    char buf[32];
    sscanf(str[1], "%33c", buf); //Noncompliant
}

In this example, buf can contain 32 char elements. Therefore, the format specifier %33c causes a buffer overflow.

Correction — Use Smaller Precision in Format Specifier

One possible correction is to read a smaller number of elements into the buffer.

#include <stdio.h>

void func (char *str[]) {
    char buf[32];
    sscanf(str[1], "%32c", buf);
}
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 certain string manipulation functions write to their destination buffer argument at an offset greater than the buffer size.

For instance, when calling the function sprintf(char* buffer, const char* format), you use a constant string format of greater size than buffer.

Risk

Buffer overflow can cause unexpected behavior such as memory corruption or stopping your system. Buffer overflow also introduces the risk of code injection.

Fix

One possible solution is to use alternative functions to constrain the number of characters written. For instance:

  • If you use sprintf to write formatted data to a string, use snprintf, _snprintf or sprintf_s instead to enforce length control. Alternatively, use asprintf to automatically allocate the memory required for the destination buffer.

  • If you use vsprintf to write formatted data from a variable argument list to a string, use vsnprintf or vsprintf_s instead to enforce length control.

  • If you use wcscpy to copy a wide string, use wcsncpy, wcslcpy, or wcscpy_s instead to enforce length control.

Another possible solution is to increase the buffer size.

Example — Buffer Overflow in sprintf Use
#include <stdio.h>

void func(void) {
    char buffer[20];
    char *fmt_string = "This is a very long string, it does not fit in the buffer";

    sprintf(buffer, fmt_string); //Noncompliant
}

In this example, buffer can contain 20 char elements but fmt_string has a greater size.

Correction — Use snprintf Instead of sprintf

One possible correction is to use the snprintf function to enforce length control.

#include <stdio.h>

void func(void) {
    char buffer[20];
    char *fmt_string = "This is a very long string, it does not fit in the buffer";

    snprintf(buffer, 20, fmt_string);
}
Issue

This issue occurs when certain string manipulation functions write to their destination buffer argument at a negative offset from the beginning of the buffer.

For instance, for the function sprintf(char* buffer, const char* format), you obtain the buffer from an operation buffer = (char*)arr; ... buffer += offset;. arr is an array and offset is a negative value.

Risk

Buffer underflow can cause unexpected behavior such as memory corruption or stopping your system. Buffer underflow also introduces the risk of code injection.

Fix

If the destination buffer argument results from pointer arithmetic, see if you are decrementing a pointer. Fix the pointer decrement by modifying either the original value before decrement or the decrement value.

Example — Buffer Underflow in sprintf Use
#include <stdio.h>
#define offset -2

void func(void) {
    char buffer[20];
    char *fmt_string ="Text";

    sprintf(&buffer[offset], fmt_string); //Noncompliant
}

In this example, &buffer[offset] is at a negative offset from the memory allocated to buffer.

Correction — Change Pointer Decrementer

One possible correction is to change the value of offset.

#include <stdio.h>
#define offset 2

void func(void) {
    char buffer[20];
    char *fmt_string ="Text";

    sprintf(&buffer[offset], fmt_string);     
}
Issue

This issue occurs when a memory library function is called with invalid arguments. For instance, the memcpy function copies to an array that cannot accommodate the number of bytes copied.

Risk

Use of a memory library function with invalid arguments can result in issues such as buffer overflow.

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:

Example — Invalid Use of Standard Library Memory Routine Error
#include <string.h>
#include <stdio.h>

char* Copy_First_Six_Letters(void)
 {
  char str1[10],str2[5];

  printf("Enter string:\n");
  scanf("%9s",str1);

  memcpy(str2,str1,6);  //Noncompliant
  /* Defect: Arguments of memcpy invalid: str2 has size < 6 */

  return str2;
 }

The size of string str2 is 5, but six characters of string str1 are copied into str2 using the memcpy function.

Correction — Call Function with Valid Arguments

One possible correction is to adjust the size of str2 so that it accommodates the characters copied with the memcpy function.

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

char* Copy_First_Six_Letters(void)
 {
  /* Fix: Declare str2 with size 6 */
  char str1[10],str2[6]; 

  printf("Enter string:\n");
  scanf("%9s",str1);

  memcpy(str2,str1,6);
  return str2;
 }
Issue

This issue occurs when a string library function is called with invalid arguments.

Risk

The risk depends on the type of invalid arguments. For instance, using the strcpy function with a source argument larger than the destination argument can result in buffer overflows.

Fix

The fix depends on the standard library function involved in the defect. In some cases, you can constrain the function arguments before the function call. For instance, if the strcpy function:

char * strcpy(char * destination, const char* source)
tries to copy too many bytes into the destination argument compared to the available buffer, constrain the source argument before the call to strcpy. In some cases, you can use an alternative function to avoid the error. For instance, instead of strcpy, you can use strncpy to control the number of bytes copied.

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 — Invalid Use of Standard Library String Routine Error
 #include <string.h>
 #include <stdio.h>
 
 char* Copy_String(void)
 {
  char *res;
  char gbuffer[5],text[20]="ABCDEFGHIJKL";

  res=strcpy(gbuffer,text);  //Noncompliant
  /* Error: Size of text is less than gbuffer */

  return(res);
 }

The string text is larger in size than gbuffer. Therefore, the function strcpy cannot copy text into gbuffer.

Correction — Use Valid Arguments

One possible correction is to declare the destination string gbuffer with equal or larger size than the source string text.

#include <string.h>
 #include <stdio.h>
 
 char* Copy_String(void)
 {
  char *res;
  /*Fix: gbuffer has equal or larger size than text */
  char gbuffer[20],text[20]="ABCDEFGHIJKL";

  res=strcpy(gbuffer,text);

  return(res);
 }
Issue

This issue occurs when you do not check the length argument and data buffer argument of memory copying functions such as memcpy, memset, or memmove, to protect against buffer overflows.

Risk

If an attacker can manipulate the data buffer or length argument, the attacker can cause buffer overflow by making the actual data size smaller than the length.

This mismatch in length allows the attacker to copy memory past the data buffer to a new location. If the extra memory contains sensitive information, the attacker can now access that data.

This defect is similar to the SSL Heartbleed bug.

Fix

When copying or manipulating memory, compute the length argument directly from the data so that the sizes match.

Example — Copy Buffer of Data
#include <stdlib.h>
#include <string.h>

typedef struct buf_mem_st {
    char *data;
    size_t max;     /* size of buffer */
} BUF_MEM;

extern BUF_MEM beta;

int cpy_data(BUF_MEM *alpha)
{
    BUF_MEM *os = alpha;
    int num, length;

    if (alpha == 0x0) return 0;
    num = 0;

    length = *os->data;
    memcpy(&(beta.data[num]), os->data + 2, length); //Noncompliant

    return(1);
}

This function copies the buffer alpha into a buffer beta. However, the length variable is not related to data+2.

Correction — Check Buffer Length

One possible correction is to check the length of your buffer against the maximum value minus 2. This check ensures that you have enough space to copy the data to the beta structure.

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

typedef struct buf_mem_st {
    char *data;
    size_t max;     /* size of buffer */
} BUF_MEM;

extern BUF_MEM beta;

int cpy_data(BUF_MEM *alpha)
{
    BUF_MEM *os = alpha;
    int num, length;

    if (alpha == 0x0) return 0;
    num = 0;

    length = *os->data;
    if (length<(os->max -2)) {
        memcpy(&(beta.data[num]), os->data + 2, length); 
    }

    return(1);

}
Issue

This issue occurs when a string does not have enough space to terminate with a null character '\0'.

This defect applies only for projects in C.

Risk

A buffer overflow can occur if you copy a string to an array without assuming the implicit null terminator.

Fix

If you initialize a character array with a literal, avoid specifying the array bounds.

char three[]  = "THREE";
The compiler automatically allocates space for a null terminator. In the preceding example, the compiler allocates sufficient space for five characters and a null terminator.

If the issue occurs after initialization, you might have to increase the size of the array by one to account for the null terminator.

In certain circumstances, you might want to initialize the character array with a sequence of characters instead of a string. In this situation, add comments to your result or code to avoid another review. See:

Example — Array size is too small
void countdown(int i)
{
    static char one[5]   = "ONE";
    static char two[5]   = "TWO";
    static char three[5] = "THREE"; //Noncompliant
}

The character array three has a size of 5 and 5 characters 'T', 'H', 'R', 'E', and 'E'. There is no room for the null character at the end because three is only five bytes large.

Correction — Increase Array Size

One possible correction is to change the array size to allow for the five characters plus a null character.

void countdown(int i)
{
    static char one[5]   = "ONE";
    static char two[5]   = "TWO";
    static char three[6] = "THREE";
}
Correction — Change Initialization Method

One possible correction is to initialize the string by leaving the array size blank. This initialization method allocates enough memory for the five characters and a terminating-null character.

void countdown(int i)
{
    static char one[5]   = "ONE";
    static char two[5]   = "TWO";
    static char three[]  = "THREE";
}
Issue

This issue occurs when a pointer is dereferenced outside its bounds.

When a pointer is assigned an address, a block of memory is associated with the pointer. You cannot access memory beyond that block using the pointer.

Risk

Dereferencing a pointer outside its bounds is undefined behavior. You can read an unpredictable value or try to access a location that is not allowed and encounter a segmentation fault.

Fix

The fix depends on the root cause of the defect. For instance, you dereferenced a pointer inside a loop and one of these situations happened:

  • The upper bound of the loop is too large.

  • You used pointer arithmetic to advance the pointer with an incorrect value for the pointer increment.

To fix the issue, you have to modify the loop bound or the pointer increment value.

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:

Example — Pointer access out of bounds error
int* Initialize(void)
{
 int arr[10];
 int *ptr=arr;

 for (int i=0; i<=9;i++)
   {
    ptr++;
    *ptr=i;   //Noncompliant
    /* Defect: ptr out of bounds for i=9 */
   }

 return(arr);
}

ptr is assigned the address arr that points to a memory block of size 10*sizeof(int). In the for-loop, ptr is incremented 10 times. In the last iteration of the loop, ptr points outside the memory block assigned to it. Therefore, it cannot be dereferenced.

Correction — Check Pointer Stays Within Bounds

One possible correction is to reverse the order of increment and dereference of ptr.

int* Initialize(void)
{
 int arr[10];
 int *ptr=arr;

 for (int i=0; i<=9;i++)
     {
      /* Fix: Dereference pointer before increment */
      *ptr=i;
      ptr++;
     }

 return(arr);
}

After the last increment, even though ptr points outside the memory block assigned to it, it is not dereferenced more.

Issue

This issue occurs when a pointer dereference uses an offset variable from an unknown or unsecure source.

This check focuses on dynamically allocated buffers. For static buffer offsets, see Array access with tainted index.

Risk

The index might be outside the valid array range. If the tainted index is outside the array range, it can cause:

  • Buffer underflow/underwrite, or writing to memory before the beginning of the buffer.

  • Buffer overflow, or writing to memory after the end of a buffer.

  • Over reading a buffer, or accessing memory after the end of the targeted buffer.

  • Under-reading a buffer, or accessing memory before the beginning of the targeted buffer.

An attacker can use an invalid read or write to compromise your program.

Fix

Validate the index before you use the variable to access the pointer. Check to make sure that the variable is inside the valid range and does not overflow.

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 — Dereference Pointer Array
#include <stdio.h>
#include <stdlib.h>
enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};
extern void read_pint(int*);

int taintedptroffset(void) {
    int offset;
    scanf("%d",&offset);
    int* pint = (int*)calloc(SIZE10, sizeof(int));
    int c = 0;
    if(pint) {
        /* Filling array */
        read_pint(pint);
        c = pint[offset];//Noncompliant
        free(pint);
    }
    return c;
}

In this example, the function initializes an integer pointer pint. The pointer is dereferenced using the input index offset. The value of offset could be outside the pointer range, causing an out-of-range error.

Correction — Check Index Before Dereference

One possible correction is to validate the value of offset. Continue with the pointer dereferencing only if offset is inside the valid range.

#include <stdlib.h>
#include <stdio.h>
enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};
extern void read_pint(int*);

int taintedptroffset(void) {
    int offset;
    scanf("%d",&offset);
    int* pint = (int*)calloc(SIZE10, sizeof(int));
    int c = 0;
    if (pint) {
        /* Filling array */
        read_pint(pint);
        if (offset>0 && offset<SIZE10) {
            c = pint[offset];
        }
        free(pint);
    }
    return c;
}
Issue

This issue occurs when Polyspace Bug Finder™ detects possibly unintended results from the use of sizeof operator. For instance:

  • You use the sizeof operator on an array parameter name, expecting the array size. However, the array parameter name by itself is a pointer. The sizeof operator returns the size of that pointer.

  • You use the sizeof operator on an array element, expecting the array size. However, the operator returns the size of the array element.

  • The size argument of certain functions such as strncmp or wcsncpy is incorrect because you used the sizeof operator earlier with possibly incorrect expectations. For instance:

    • In a function call strncmp(string1, string2, num), num is obtained from an incorrect use of the sizeof operator on a pointer.

    • In a function call wcsncpy(destination, source, num), num is the not the number of wide characters but a size in bytes obtained by using the sizeof operator. For instance, you use wcsncpy(destination, source, sizeof(destination) - 1) instead of wcsncpy(destination, source, (sizeof(desintation)/sizeof(wchar_t)) - 1).

Risk

Incorrect use of the sizeof operator can cause the following issues:

  • If you expect the sizeof operator to return array size and use the return value to constrain a loop, the number of loop runs are smaller than what you expect.

  • If you use the return value of sizeof operator to allocate a buffer, the buffer size is smaller than what you require. Insufficient buffer can lead to resultant weaknesses such as buffer overflows.

  • If you use the return value of sizeof operator incorrectly in a function call, the function does not behave as you expect.

Fix

Possible fixes are:

  • Do not use the sizeof operator on an array parameter name or array element to determine array size.

    The best practice is to pass the array size as a separate function parameter and use that parameter in the function body.

  • Use the sizeof operator carefully to determine the number argument of functions such as strncmp or wcsncpy. For instance, for wide string functions such as wcsncpy, use the number of wide characters as argument instead of the number of bytes.

Example — sizeof Used Incorrectly to Determine Array Size
#define MAX_SIZE 1024

void func(int a[MAX_SIZE]) {
    int i;

    for (i = 0; i < sizeof(a)/sizeof(int); i++)  //Noncompliant
    {
        a[i] = i + 1;
    }
}

In this example, sizeof(a) returns the size of the pointer a and not the array size.

Correction — Determine Array Size in Another Way

One possible correction is to use another means to determine the array size.

#define MAX_SIZE 1024

void func(int a[MAX_SIZE]) {
    int i;

    for (i = 0; i < MAX_SIZE; i++)    {
        a[i] = i + 1;
    }
}
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));
    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.

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));
    if(!aGroupWithID) {
        free(aGroup);
        /*Handle error*/
    }
    
    memset(aGroupWithID, 0 , sizeof(struct groupWithID));
    /* Reinitialize group */
    if(aGroupWithID -> groupSize > 0) { 
        /* ... */
    }
    
    /* ...*/
    free(aGroupWithID);
}

Issue

This issue occurs when strings from unsecure sources are used in string manipulation routines that implicitly dereference the string buffer, for instance, strcpy or sprintf.

Tainted NULL or non-null-terminated string raises no defect for a string returned from a call to scanf-family variadic functions. Similarly, no defect is raised when you pass the string with a %s specifier to printf-family variadic functions.

Risk

If a string is from an unsecure source, it is possible that an attacker manipulated the string or pointed the string pointer to a different memory location.

If the string is NULL, the string routine cannot dereference the string, causing the program to crash. If the string is not null-terminated, the string routine might not know when the string ends. This error can cause you to write out of bounds, causing a buffer overflow.

Fix

Validate the string before you use it. Check that:

  • The string is not NULL.

  • The string is null-terminated

  • The size of the string matches the expected 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 — Getting String from Input
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SIZE128 128
#define MAX 40
extern void print_str(const char*);
void warningMsg(void)
{
	char userstr[MAX];
	read(0,userstr,MAX);
	char str[SIZE128] = "Warning: ";
	strncat(str, userstr, SIZE128-(strlen(str)+1));//Noncompliant
	print_str(str);
}


In this example, the string str is concatenated with the argument userstr. The value of userstr is unknown. If the size of userstr is greater than the space available, the concatenation overflows.

Correction — Validate the Data

One possible correction is to check the size of userstr and make sure that the string is null-terminated before using it in strncat. This example uses a helper function, sansitize_str, to validate the string. The defects are concentrated in this function.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SIZE128 128
#define MAX 40
extern void print_str(const char*);
int sanitize_str(char* s) {
	int res = 0; 
	if (s && (strlen(s) > 0)) { // Noncompliant-TAINTED_STRING only flagged here
		// - string is not null
		// - string has a positive and limited size
		// - TAINTED_STRING on strlen used as a firewall
		res = 1;
	}
	return res; 
}
void warningMsg(void)
{
	char userstr[MAX];
	read(0,userstr,MAX);
	char str[SIZE128] = "Warning: ";
	if (sanitize_str(userstr))	
		strncat(str, userstr, SIZE128-(strlen(str)+1));
	print_str(str);
}
Correction — Validate the Data

Another possible correction is to call function errorMsg and warningMsg with specific strings.

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

#define SIZE128 128

extern void print_str(const char*);

void warningMsg(char* userstr)
{
    char str[SIZE128] = "Warning: ";
    strncat(str, userstr, SIZE128-(strlen(str)+1));
    print_str(str);
}

void errorMsg(char* userstr)
{
  char str[SIZE128] = "Error: ";
  strncat(str, userstr, SIZE128-(strlen(str)+1));
  print_str(str);
}

int manageSensorValue(int sensorValue) {
  int ret = sensorValue;
  if ( sensorValue < 0 ) {
    errorMsg("sensor value should be positive");
    exit(1);
  } else if ( sensorValue > 50 ) {
    warningMsg("sensor value greater than 50 (applying threshold)...");
    sensorValue = 50;
  }
  
  return sensorValue;
}
Issue

This issue occurs when your code uses standard functions that write data to a buffer in a way that can result in buffer overflows.

The following table lists dangerous standard functions, the risks of using each function, and what function to use instead. The checker flags:

  • Any use of an inherently dangerous function.

  • An use of a possibly dangerous function only if the size of the buffer to which data is written can be determined at compile time. The checker does not flag an use of such a function with a dynamically allocated buffer.

Dangerous FunctionRisk LevelSafer Function
getsInherently dangerous — You cannot control the length of input from the console.fgets
std::cin::operator>> and std::wcin::operator>>Inherently dangerous — You cannot control the length of input from the console.

Preface calls to cin by cin.width to control the input length. This method can result in truncated input.

To avoid potential buffer overflow and truncated input, use std::string objects as destinations for >> operator.

strcpyPossibly dangerous — If the size of the destination buffer is too small to accommodate the source buffer and a null terminator, a buffer overflow might occur. Use the function strlen() to determine the size of the source buffer, and allocate sufficient memory so that the destination buffer can accommodate the source buffer and a null terminator. Instead of strcpy, use the function strncpy.
stpcpyPossibly dangerous — If the source length is greater than the destination, buffer overflow can occur.stpncpy
lstrcpy or StrCpyPossibly dangerous — If the source length is greater than the destination, buffer overflow can occur.StringCbCopy, StringCchCopy, strncpy, strcpy_s, or strlcpy
strcatPossibly dangerous — If the concatenated result is greater than the destination, buffer overflow can occur.strncat, strlcat, or strcat_s
lstrcat or StrCatPossibly dangerous — If the concatenated result is greater than the destination, buffer overflow can occur.StringCbCat, StringCchCat, strncay, strcat_s, or strlcat
wcpcpyPossibly dangerous — If the source length is greater than the destination, buffer overflow can occur.wcpncpy
wcscatPossibly dangerous — If the concatenated result is greater than the destination, buffer overflow can occur.wcsncat, wcslcat, or wcncat_s
wcscpyPossibly dangerous — If the source length is greater than the destination, buffer overflow can occur.wcsncpy
sprintfPossibly dangerous — If the output length depends on unknown lengths or values, buffer overflow can occur.snprintf
vsprintfPossibly dangerous — If the output length depends on unknown lengths or values, buffer overflow can occur.vsnprintf
Risk

These functions can cause buffer overflow, which attackers can use to infiltrate your program.

Fix

The fix depends on the root cause of the defect. See fixes in the table above and code examples with fixes below.

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

Example — Using sprintf
#include <stdio.h>
#include <string.h>
#include <iostream>

#define BUFF_SIZE 128


int dangerous_func(char *str)
{
    char dst[BUFF_SIZE];
    int r = 0;

    if (sprintf(dst, "%s", str) == 1) //Noncompliant
    {
        r += 1;
        dst[BUFF_SIZE-1] = '\0';
    }
    
    return r;
}

This example function uses sprintf to copy the string str to dst. However, if str is larger than the buffer, sprintf can cause buffer overflow.

Correction — Use snprintf with Buffer Size

One possible correction is to use snprintf instead and specify a buffer size.

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

#define BUFF_SIZE 128


int dangerous_func(char *str)
{
    char dst[BUFF_SIZE];
    int r = 0;

    if (snprintf(dst, sizeof(dst), "%s", str) == 1)
    {
        r += 1;
        dst[BUFF_SIZE-1] = '\0';
    }
    
    return r;
}
Issue

This issue occurs when you do not check if a write operation using an fgets-family function such as:

char * fgets(char* buf, int n, FILE *stream)
succeeded and the buffer written has valid content, or you do not reset the buffer on failure. You then perform an operation that assumes a buffer with valid content. For instance, if the buffer with possibly indeterminate content is buf (as shown above), the checker raises a defect if:

  • You pass buf as argument to standard functions that print or manipulate strings or wide strings.

  • You return buf from a function.

  • You pass buf as argument to external functions with parameter type const char * or const wchar_t *.

  • You read buf as buf[index] or *(buf + offset), where index or offset is a numerical value representing the distance from the beginning of the buffer.

Risk

If an fgets-family function fails, the content of its output buffer is indeterminate. Use of such a buffer has undefined behavior and can result in a program that stops working or other security vulnerabilities.

Fix

Reset the output buffer of an fgets-family function to a known string value when the function fails.

Example — Output of fgets() Passed to External Function
#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <stdlib.h>

#define SIZE20 20

extern void display_text(const char *txt);

void func(void) {
    char buf[SIZE20];
	
	/* Check fgets() error */
    if (fgets (buf, sizeof (buf), stdin) == NULL)
    {
        /* 'buf' may contain an indeterminate string.  */
        ;
    }
	/* 'buf passed to external function */
    display_text(buf);  //Noncompliant
}
        
      

In this example, the output buf is passed to the external function display_text(), but its value is not reset if fgets() fails.

Correction — Reset fgets() Output on Failure

If fgets() fails, reset buf to a known value before you pass it to an external function.

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

#define SIZE20 20

extern void display_text(const char *txt);

void func1(void) {
    char buf[SIZE20];
	/* Check fgets() error */
    if (fgets (buf, sizeof (buf), stdin) == NULL)
    {
		/* value of 'buf' reset after fgets() failure. */
        buf[0] = '\0';
    }
	/* 'buf' passed to external function */
    display_text(buf); 
} 

Check Information

Category: Others

Version History

Introduced in R2023a

expand all