...
This example uses the getchar()
function to read in a character at a time from stdin
, instead of reading the entire line at once. The stdin
stream is read until end-of-file is encountered or a new-line character is read. Any new-line character is discarded, and a null character is written immediately after the last character read into the array. Similar to the previous example, there are no guarantees that this code will not result in a buffer overflow.
Code Block |
---|
|
char buf[BUFSIZ], *p;
int ch;
p = buf;
while ( ((ch = getchar()) != '\n')
&& !feof(stdin)
&& !ferror(stdin))
{
*p++ = ch;
}
*p++ = 0;
|
...
In this compliant solution, characters are no longer copied to buf
once index = BUFSIZ
, leaving room to null terminate the string. The loop continues to read through to the end of the line until the end of the file is encountered or an error occurs.
Code Block |
---|
|
unsigned char buf[BUFSIZ];
int ch;
int index = 0;
int chars_read = 0;
while ( ( (ch = getchar()) != '\n')
&& !feof(stdin)
&& !ferror(stderr) )
{
if (index < BUFSIZ-1) {
buf[index++] = (unsigned char)ch;
}
chars_read++;
} /* end while */
buf[index] = '\0'; /* terminate NTBS */
if (feof(stdin)) {
/* handle EOF */
}
if (ferror(stdin)) {
/* handle error */
}
if (chars_read > index) {
/* handle truncation */
}
|
...
Wiki Markup |
---|
According to Section 7.19.7.7 of C99 \[[ISO/IEC 9899:1999|AA. C++ References#ISO/IEC 9899-1999]\], the {{gets()}} function reads characters from the {{stdin}} into a destination array until end-of-file is encountered or a new-line character is read. Any new-line character is discarded, and a null character is written immediately after the last character read into the array. |
Code Block |
---|
|
char buf[BUFSIZ];
if (gets(buf) == NULL) {
/* Handle Error */
}
|
...
The fgets()
function reads, at most, one less than a specified number of characters from a stream into an array. This example is compliant because the number of bytes copied from stdin
to buf
cannot exceed the allocated memory.
Code Block |
---|
|
char buf[BUFSIZ];
int ch;
char *p;
if (fgets(buf, sizeof(buf), stdin)) {
/* fgets succeeds, scan for newline character */
p = strchr(buf, '\n');
if (p) {
*p = '\0';
}
else {
/* newline not found, flush stdin to end of line */
while (((ch = getchar()) != '\n')
&& !feof(stdin)
&& !ferror(stdin)
);
}
}
else {
/* fgets failed, handle error */
}
|
...
Wiki Markup |
---|
According to TR 24731 \[[ISO/IEC TR 24731-2006|AA. C++ References#ISO/IEC TR 24731-2006]\]: |
...
<blockquote><p>No additional characters are read after a new-line character (which is discarded) or after end-of-file. The discarded new-line character does not count towards number of characters read. A null character is written immediately after the last character read into the array. |
...
If
</p></blockquote>If end-of-file is encountered and no characters have been read into the destination array, or if a read error occurs during the operation, then the first character in the destination array is set to the null character and the other elements of the array take unspecified values. |
Code Block |
---|
|
char buf[BUFSIZ];
if (gets_s(buf, sizeof(buf)) == NULL) {
/* handle error */
}
|
...
The scanf()
function is used to read and format input from stdin
. Improper use of scanf()
may result in an unbounded copy. In the code below, the call to scanf()
does not limit the amount of data read into buf
. If more than 9 characters are read, then a buffer overflow occurs.
Code Block |
---|
|
enum { CHARS_TO_READ = 9 };
char buf[CHARS_TO_READ + 1];
scanf("%s", buf);
|
...
The number of characters read by scanf()
can be bounded by using the format specifier supplied to scanf()
.
Code Block |
---|
|
#define STRING(n) STRING_AGAIN(n)
#define STRING_AGAIN(n) #n
#define CHARS_TO_READ 9
char buf[CHARS_TO_READ + 1];
scanf("%"STRING(CHARS_TO_READ)"s", buf);
|
Non-Compliant Code Example (operator<<()
)
Inputting more than 11 characters into the following the C++ program results in an out-of-bounds write:Since the input is unbounded, the following code could lead to a buffer overflow
Code Block |
---|
|
#include <iostream>
int main(void) {
using namespace std;
char buf[12];
char buf[12];
cin >> buf;
|
To solve this problem, one can be tempted to use the width method of the ios_base
class, but there still is a trap.
Code Block |
---|
|
char buf_one[12];
char buf_two[12];
cin.width(12);
cin >> buf_one;
cin >> buf_two;
|
Wiki Markup |
---|
In this example, the first read won't overflow, but the second still could, because as the C++ |
...
...
...
...
operator>> extracts characters and stores them into successive locations of an array \[...\] operator>> then calls width(0)." Which means that width should be called every time you use the >> operator with a bounded array. |
Arguably better code
While the following doesn't suffer of the same problem as the previous, it still has some :
Compliant Solution (operator<<()
)
The extraction operation can be limited to a specified number of characters if ios_base::width
is set to a value > 0. In this compliant solution, the width
field is set to the size of the buffer. In this particular example, this limits the number of characters read from stdin
to 11 characters. The call to the extraction operator appends a null-termination character to the character array.
Code Block |
---|
|
#include <iostream>
int main(void) {
using namespace std;
char buf_one[12];
char buf_two[12];
cin.width(12);
cin >> buf_one;
cin.width(12);
cin >> buf_two;
|
Wiki Markup |
---|
because, as the |
...
C++ standard states, "If width() is greater than zero, n is width() \[...\] n-1 characters are stored \[...\] Operator>> then stores a null byte (charT()) in the next position, which may be the first position if no characters were extracted." The input could therefore be truncated, leading to information lost, and to a possible vulnerability.
In this particular example, if the user enters a string longer than 11 (11 characters + the NULL terminating character automatically appended by the >> operator equals 12 characters), the 12th and all subsequent characters will be lost. |
To avoid this truncation problem, it would be better to use an instance of the string
class to store the input, as it is dynamically resized to fit the input.
Code Block |
---|
|
string input;
cin >> input;
const char *array = input.c_str();
|
The only problem with this code is that it will be necessary to copy the characters in another array if they are to be modifiedAfter a call to the extraction operation, the value of the width
field is reset to 0.
Risk Assessment
Copying data from an unbounded source to a buffer of fixed size may result in a buffer overflow.
...