Contenido principal

CWE Rule 805

Buffer Access with Incorrect Length Value

Since R2023a

Description

The software uses a sequential operation to read or write a buffer, but it uses an incorrect length value that causes it to access memory that is outside of the bounds of the buffer.

Polyspace Implementation

The rule checker checks for these issues:

  • Buffer overflow from incorrect string format specifier

  • 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

  • Possible misuse of sizeof

  • String Operations On Null Pointer

  • Invalid arguments in fread()

Examples

expand all

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

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 Polyspace Bug Finder Results in Polyspace Platform 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 = *(unsigned short *)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 = *(unsigned short *)os->data;
    if (length<(os->max -2)) {
        memcpy(&(beta.data[num]), os->data + 2, length); 
    }

    return(1);

}
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 perform string operations that require calling std::char_traits::length() on NULL, 0, or nullptr. Examples of such string operations are creating, appending, assigning, inserting, or replacing the string. For a list of operations that results in call to std::char_traits::length(), see STR51-CPP.

    This issue is a specific instance of the issue Null pointer, which causes violations of CERT C++: EXP34-C. Consider this code:

    std::string getString(); //returns nullptr
    void foo(){
        std::string str{getString()};//Defect
    }
    Construction of str requires an implicit call to std::char_traits::length(). Polyspace reports a violation because getString() returns a nullptr, which results in calling std::char_traits::length() on nullptr.

  • You perform certain string operations on a nonnull pointer to an uninitialized memory block. Consider this code:

    void foo() {
    
    	const char* uninitialized = (const char*)std::malloc(size*sizeof(char) + 1);;
    	 std::string tmp(uninitialized);  //Noncompliant
    } 
    Polyspace reports a violation of this rule when tmp is constructed by using the uninitialized memory in uninitialized.

A violation of this rule is not reported for stubbed functions.

Risk

Performing string operations that require calling std::char_traits::length() on NULL, 0, or nullptr might result in an undefined behavior. The function std::char_traits::length() dereferences the null pointer, which is an undefined behavior.

Performing string operations on uninitialized memory results in unexpected outcome and might result in bugs that are difficult to diagnose.

Fix

Check if the string object is a null pointer or an empty string before you perform string operations.

Example — String Operations Using Null Pointer

          #include <cstdlib>
#include <string>

int status;
const char *getInput()
{
    return status == 0 ? std::getenv("TMP") : nullptr;

}

void foo()
{
    status=1;
    const char *data = getInput();
    //...
    std::string str(data);   // Noncompliant
    str.append(data);        // Noncompliant
    str.assign(data);        // Noncompliant
    str.insert(0, data);     // Noncompliant
    str.replace(0, 1, data); // Noncompliant
}

In this example, the const char* object data is created by calling getInput(), and then various string operations are performed by using data. Polyspace reports a violation of this rule for each string operation because the function getInput() returns a nullptr which is then assigned to data.

Correction — Avoid Performing String Operation on Null Pointers

Modify getInput() so that the function does not return a nullptr.


          #include <cstdlib>
#include <string>

int status;
const char *getInput()
{
    return status == 0 ? std::getenv("TMP") : "";

}

void foo()
{
    status=1;
    const char *data = getInput();
    //...
    std::string str(data);   // Compliant
    str.append(data);        // Compliant
    str.assign(data);        // Compliant
    str.insert(0, data);     // Compliant
    str.replace(0, 1, data); // Compliant
}
Issue

This issue occurs you invoke fread() using a set of arguments that results in a buffer overflow. Consider the syntax of fread():

size_t fread( void *restrict buffer, size_t size, size_t count, FILE *restrict stream );
When invoking fread(), the size of the memory block pointed to by buffer must be able to hold count number of elements of size size. If buffer cannot hold count elements of size, a buffer overflow occurs. Polyspace reports a violation if the size and count parameters are incompatible with buffer when invoking fread().

Risk

Calling fread() when the number of bytes indicated by size * count is larger than the size of buffer results in a buffer overflow, which is undefined behavior.

Fix

When calling fread(), check that size * count <= sizeof(buffer).

Example

In this example, fread() is called to read contents of the filestream file into the buffer buf. When calculating the parameters of fread(), this function sets the numel parameter to the number of bytes in buf (sizeof(buf)). But the variable sz holds the size of a wide character(sizeof(*buf)), which can be more than 1 byte. Thus, the memory requirement imposed by numel and sz exceeds the capacity of buf. This function call can result in a buffer overflow and undefined behavior. Polyspace reports a violation.

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <wchar.h>

#define BUFSZ 1024
void foo(FILE *file){
    //...
    // Create buffer
	wchar_t buf[BUFSZ];
    // calculate size of each element
	const size_t sz = sizeof(*buf);
    // count the number of elements
	const size_t numel = sizeof(buf);
    // Call fread to read from *file into buffer
	size_t nread = fread(buf, sz, numel, file);   // Noncompliant      

	/* ... */

}

Correction

To fix this issue, check that the numel and sz parameters are calculated correctly. For example, calculate the numel parameter with the correct assumption about the size of the elements:

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <wchar.h>

#define BUFSZ 1024
void foo(FILE *file){
    //...
    // Create buffer
	wchar_t buf[BUFSZ];
    // calculate size of each element
	const size_t sz = sizeof(*buf);
    // count the number of elements
	const size_t numel = sizeof(buf)/sz;
    // Call fread to read from *file into buffer
	size_t nread = fread(buf, sz, numel, file);   // Compliant      

	/* ... */

}

Check Information

Category: Memory Buffer Errors
PQL Name: std.cwe_native.R805

Version History

Introduced in R2023a