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 79 [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());
// ...
}
|