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 character data to be copied and the null-termination character. This rule is a C++-specific instance of STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator. 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:.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> void f() { char buf[12]; std::cin >> buf; } |
...
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. In
Code Block | ||||
---|---|---|---|---|
| ||||
#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 of operator>>(basic_istream &, charT *)
and, and states in part [ISO/IEC 14882-2014], 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 calls then callswidth(0)
.
Consequently, it is necessary to call call width()
prior to each each operator>>
call call passing a bounded array.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream>
void f() {
char bufOne[12];
char bufTwo[12];
std::cin.width(12);
std::cin >> bufOne;
std::cin >> bufTwo;
} |
Noncompliant Code Example
The following noncompliant code example calls std::ios_base_width()
prior to each call to operator>>()
. However, it still However, this does not account for the input being truncated. Only the first 11 characters are read from the standard input stream, and a null terminator is then appended. The input could therefore be truncated, leading , which may lead to information loss or a possible vulnerability.
...
...
#include <iostream>
void f() {
char bufOne[12];
char bufTwo[12];
std::cin.width(12);
std::cin >> bufOne;
std::cin.width(12);
std::cin >> bufTwo;
}
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:.
Code Block | ||||
---|---|---|---|---|
| ||||
#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.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <fstream> #include <string> void f(std::istream &in) { char buffer[32]; try { in.read(buffer, 32sizeof(buffer)); } catch (std::ios_base::failure &e) { // Handle error } std::string str(buffer); // ... } |
Compliant Solution
This compliant solution continues to assume assumes that the input from the file is exactly at most 32 characters, and instead . Instead of inserting a null terminator, it constructs the std::string
object based on the sizenumber 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.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <fstream> #include <string> void f(std::istream &in) { char buffer[32]; try { in.read(buffer, 32sizeof(buffer)); } catch (std::ios_base::failure &e) { // Handle error } std::string str(buffer, 32in.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 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| stream-input-char-array | Partially checked + soundly supported | ||||||
CodeSonar |
| MISC.MEM.NTERM LANG.MEM.BO | No space for null terminator Buffer overrun | ||||||
Helix QAC |
| C++5216 DF2835, DF2836, DF2839, | |||||||
Klocwork |
| NNTS.MIGHT NNTS.TAINTED NNTS.MUST SV.UNBOUND_STRING_INPUT.CIN | |||||||
LDRA tool suite |
| 489 S, 66 X, 70 X, 71 X | Partially implemented | ||||||
Parasoft C/C++test |
| CERT_CPP-STR50-b | Avoid overflow due to reading a not zero terminated string | |||||||
Polyspace Bug Finder |
| CERT C++: STR50-CPP | Checks for:
Rule partially covered. | ||||||
RuleChecker |
| stream-input-char-array | Partially checked | ||||||
SonarQube C/C++ Plugin |
| S3519 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
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, " |
[Seacord 2013] | Chapter 2, "Strings" |
...
...