Main Content

CERT C++: STR50-CPP

Guarantee that storage for strings has sufficient space for character data and the null terminator

Description

Rule Definition

Guarantee that storage for strings has sufficient space for character data and the null terminator.1

Polyspace Implementation

The rule checker checks for these issues:

  • Use of dangerous standard function.

  • Buffer overflow from incorrect string format specifier.

  • Destination buffer overflow in string manipulation.

  • Insufficient destination buffer size.

  • Input string not null-terminated

Examples

expand all

Issue

The Use of dangerous standard function check highlights uses of functions that are inherently dangerous or potentially dangerous given certain circumstances. The following table lists possibly dangerous functions, the risks of using each function, and what function to use instead.

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 source length is greater than the destination, buffer overflow can occur.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. Often the result details 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 the event history, you can trace back using right-click options in the source code and see previous related events. See also Interpret Bug Finder Results in Polyspace Desktop User Interface.

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

Buffer overflow from incorrect string format specifier 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 use a smaller precision in the format specifier.

#include <stdio.h>

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

Destination buffer overflow in string manipulation 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

Insufficient destination buffer size occurs when the destination buffer in a strcpy operation cannot accommodate the source buffer and a null terminator. This issue is reported if the size of the source buffer is unknown. Consider this code:

int main (int argc, char *argv[])
{
  const char *const input = ((argc && argv[0]) ? argv[0] : "");
  char str[100];
  strcpy(input, str); // Noncompliant
}
In this case, the size of the source buffer input is unknown. The size of the destination buffer str might be smaller than the value (strlen(input)+1). Polyspace® reports a violation on the strcpy operation.

Risk

Using a destination buffer of insufficient size might allow an attacker to cause a buffer overflow. In the preceding code example, if argv[0] contains 100 or more characters, the strcpy operation results in a buffer overflow.

Fix

Before calling the function strcpy(), allocate sufficient memory dynamically. For instance, use the function strlen() to determine the size of the source buffer and then allocate the destination buffer so that its size is greater than the value strlen(source) + 1.

Example — Destination Buffer Too Small

In this example, the size of the source buffer is unknown, while the size of the destination buffer is fixed at 128. The size of the destination buffer might not be sufficient to accommodate the characters from the source buffer and terminate the buffer with a null. Polyspace reports a violation of the rule.

#include <cstring>

int main(int argc, char *argv[]) {
	const char *const source = (argc && argv[0]) ? argv[0] : "";
	char destination[128];
	strcpy(const_cast<char*>(source), destination);//Noncompliant
	return 0;
}
Correction — Allocate Sufficient Memory for Destination Buffer

This violation is resolved by allocating sufficient memory for the destination buffer. For instance, use the function strlen() to calculate the size of the source buffer and allocate sufficient memory for the destination buffer so that it can accommodate all characters from the source buffer and the null terminator ('\0' ).

#include <cstring>
#include <cstdlib>

int main(int argc, char *argv[]) {
	const char *const source = (argc && argv[0]) ? argv[0] : "";
	char* destination = (char *)malloc(strlen(source)+ 1);
	if(destination!=NULL){
		strcpy(const_cast<char*>(source), destination);//Compliant
	}else{
		/*Handle Error*/
	}
	//...
	free(destination);
	return 0;
}
Issue

The issue Input string not null-terminated occurs when both of these conditions are true:

  • You obtain a string from an input function that does not necessarily terminates the string by a null character.

  • You use the input string that does not have the terminating null character in places where a null-terminated string is expected.

For instance:

    char str[10];
    scanf("%10c", str);
    std::string S(str);//Null-terminated string expected
Here, the constructor of std::string expects a string terminated by a null character, but str is not null-terminated. Polyspace reports a violation of this rule.

Risk

Functions in the C++ library assume that a valid string is terminated by a null character and apply this assumption string algorithms. Using these functions and algorithms with a string that lacks a terminating null character results in undefined behavior.

Fix

The fix for this issue depends on the context:

  • You can fix this issue by terminating raw input strings by a null. For example, this code declares str with 11 elements. After filling the first 10 elements with the raw input, assign the last element the value '\0'. After manually terminating the input string with a null character, you can use the input string with standard template library (STL) string functions without violating this rule.

        char str[11];
        scanf("%10c", str);
        str[10] = '\0';
        std::string S(str);//Valid string

  • Some C++ functions accepts character pointers or substrings as input. These functions can correctly handle a raw character array without a terminating null character. For instance, This code constructs the std::string object S by using a nondefault constructor. This constructor allows the use of str, which lacks a terminating null character. Such uses do not violate this rule.

        char str[10];
        scanf("%10c", str);
        std::string S(str,sizeof(str));//Valid use of substring

Example — Print Input String Without Terminating Null Character

In this example, the read function accepts an array of characters, which is then stored in the char array buffer. Because bufferdoes not have a terminating null character, using buffer with the function printf() results in undefined behavior. Polyspace reports a violation of this rule.

#include <iostream>
#include <string>
#include <fstream>
#include <cstdio>
void echo_instream(std::istream& instream) {
    //...
    char buffer[10];
    instream.read(buffer,sizeof(buffer));
	//...
    printf("%s", buffer); //Noncompliant
	//...
}

Correction — Terminate Input String with '\0'

To fix this issue, manually add the terminating null character at the end of buffer.

#include <iostream>
#include <string>
#include <fstream>
#include <cstdio>
void echo_instream(std::istream& instream) {
    //...
    char buffer[10];
    instream.read(buffer,sizeof(buffer)-1);
    buffer[9] = '\0';
	//...
    printf("%s", buffer); //Compliant
	//...
}

Check Information

Group: 05. Characters and Strings (STR)

Version History

Introduced in R2019a

expand all


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.