CertC++-STR50¶
Guarantee that storage for strings has sufficient space for character data and the null terminator
Required inputs: IR
Copying data to a buffer that is not large enough to hold that data results in
a buffer overflow. Buffer overflows occur frequently when manipulating strings
[
Seacord
2013]. To prevent such errors, either limit copies through truncation
or, preferably, ensure that the destination is of sufficient size to hold the
data to be copied. C-style strings require a null character to indicate the end
of the string, while the C++
std::basic_string template requires no such character.
Noncompliant Code Example
Because the input is unbounded, the following code could lead to a buffer overflow.
#include <iostream>
void f() {
char buf[12];
std::cin >> buf;
}
Noncompliant Code Example
To solve this problem, it may be tempting to use the
std::ios_base::width() method, but there still is a trap, as
shown in this noncompliant code example.
#include <iostream>
void f() {
char bufOne[12];
char bufTwo[12];
std::cin.width(12);
std::cin >> bufOne;
std::cin >> bufTwo;
}
In this example, the first read will not overflow, but could fill
bufOne with a truncated string. Furthermore, the second read still
could overflow
bufTwo. The C++ Standard, [istream.extractors], paragraphs 7-9
[
ISO/IEC
14882-2014], describes the behavior of
operator>>(basic_istream &, charT *) and, in part,
states the following:
operator>>then stores a null byte (charT()) in the next position, which may be the first position if no characters were extracted.operator>>then callswidth(0).
Consequently, it is necessary to call
width() prior to each
operator>> call passing a bounded array. However, this
does not account for the input being truncated, which may lead to information
loss or a possible
vulnerability.
Compliant Solution
The best solution for ensuring that data is not truncated and for guarding
against buffer overflows is to use
std::string instead of a bounded array, as in this compliant
solution.
#include <iostream>
#include <string>
void f() {
std::string input;
std::string stringOne, stringTwo;
std::cin >> stringOne >> stringTwo;
}
Noncompliant Code Example
In this noncompliant example, the unformatted input function
std::basic_istream<T>::read() is used to read an unformatted
character array of 32 characters from the given file. However, the
read() function does not guarantee that the string will be null
terminated, so the subsequent call of the
std::string constructor results in
undefined
behavior if the character array does not contain a null terminator.
#include <fstream>
#include <string>
void f(std::istream &in) {
char buffer[32];
try {
in.read(buffer, sizeof(buffer));
} catch (std::ios_base::failure &e) {
// Handle error
}
std::string str(buffer);
// ...
}
Compliant Solution
This compliant solution assumes that the input from the file is at most 32
characters. Instead of inserting a null terminator, it constructs the
std::string object based on the number of characters read from the
input stream. If the size of the input is uncertain, it is better to use
std::basic_istream<T>::readsome() or a formatted input
function, depending on need.
#include <fstream>
#include <string>
void f(std::istream &in) {
char buffer[32];
try {
in.read(buffer, sizeof(buffer));
} catch (std::ios_base::failure &e) {
// Handle error
}
std::string str(buffer, in.gcount());
// ...
}
Risk Assessment
Copying string data to a buffer that is too small to hold that data results in a buffer overflow. Attackers can exploit this condition to execute arbitrary code with the permissions of the vulnerable process.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| STR50-CPP | High | Likely | Medium | P18 | L1 |
Related Guidelines
| SEI CERT C Coding Standard | STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator |
Bibliography
| [ ISO/IEC 14882-2014] | Subclause 27.7.2.2.3, "
basic_istream::operator>>"Subclause 27.7.2.3, "Unformatted Input Functions" |
| [ Seacord 2013] | Chapter 2, "Strings" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
non-null-termination |
Do not pass a non-null-terminated character sequence to a library function that expects a string. |
None |
False |
unbound_input_to_array |
Ensure sufficient storage space for unbound input. |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
excluded_arguments¶
excluded_arguments
Arguments that should not be checked for entries in functions_under_test; first argument/parameter has index 0.Type: dict[bauhaus.analysis.config.QualifiedName, set[int]]
Default:
{ 'snprintf': {0}, 'sprintf': {0}, 'strcpy': {0}, 'strncpy': {0}, 'strxfrm': {0}, 'vsprintf': {0}, 'wcscpy': {0}, 'wcsncpy': {0} }
functions_under_test¶
functions_under_test : set[bauhaus.analysis.config.QualifiedName] = {'std::basic_string::basic_string'}
char,
int, etc.) are checked. You can use
excluded_arguments if you want to restrict the
considered arguments.
use_static_semantic_analysis¶
use_static_semantic_analysis : bool = True
StaticSemanticAnalysis
to be enabled, but will not produce any additional results if it is not.