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. (See STR03-C. Do not inadvertently truncate a string.)
Because strings are represented as arrays of characters, this rule is an instance of related to both ARR30-C. Do not form or use out of bounds pointers or array subscripts and ARR38-C. Guarantee that library functions do not form invalid pointers.
...
This noncompliant code example demonstrates what is commonly called an off-by-one error [Dowd 2006]. The loop copies data from src
to dest
. However, the null terminator may incorrectly be written 1 byte past the end of dest
because the loop does not account for the null-termination character that must be appended to dest
.
...
Compliant Solution (Off-by-One Error)
To correct In this examplecompliant solution, the loop termination condition must be is modified to account for the null-termination character that is appended to dest
:
...
The fgets()
function, however, is not a strict replacement for the gets()
function because fgets()
retains the newline character (if read) but and may also return a partial line. It is possible to use fgets()
to safely process input lines too long to store in the destination array, but this is not recommended for performance reasons. Consider using one of the following compliant solutions when replacing gets()
.
...
Compliant Solution (getline()
, POSIX)
The getline()
function behaves similarly to is similar to the fgets()
but offers several extra features. First, if the input line is too long, it resizes the buffer, using realloc()
, rather than truncating the input. Second, if successful, it function but dynamically allocates memory for the input buffer. If passed a null pointer, getline()
dynamically allocates a buffer of sufficient size to hold the input. If instead, you pass a pointer to dynamically allocated storage that is too small to hold the contents of the string, the getline()
function resizes the buffer, using realloc()
, rather than truncating the input. If successful, the getline()
function returns the number of characters read, which is useful in determining whether can be used to determine if the input has any null characters before the newline. The getline()
function works only with dynamically allocated buffers. Allocated memory must be explicitly deallocated by the caller to avoid memory leaks (see MEM31-C. Free dynamically allocated memory when no longer needed.)
Code Block | ||||
---|---|---|---|---|
| ||||
Code Block | ||||
| ||||
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void func(void) {
int ch;
size_t buffer_size = 32;
char *buffer = malloc(buffer_size);
if (!buffer) {
/* Handle error */
return;
}
if ((ssize_t size = getline(&buffer, &buffer_size, stdin))
== -1) {
/* Handle error */
} else {
char *p = strchr(buffer, '\n');
if (p) {
*p = '\0';
} else {
/* Newline not found; flush stdin to end of line */
while (((ch = getchar()) != '\n')
&& !feof(stdin)
&& !ferror(stdin))
;
}
}
free (buffer);
} |
...
( |
...
buffer |
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h>
enum { CHARS_TO_READ = 9 };
void func(void) {
char buf[CHARS_TO_READ + 1];
scanf("%9s", buf);
} |
Noncompliant Code Example (getchar()
)
...