Wiki Markup |
---|
Copying data to a buffer that is not large enough to hold that data results in a buffer overflow. While not limited to NULLnull-terminated byte strings (NTBS), thisbuffer type ofoverflows error often occurs when manipulating NTBS data. To prevent such errors, limit copies either through truncation (although consult \[[STR03-A. Do not inadvertently truncate a NULLnull-terminated byte string]\] for problems that may cause) or, preferably, ensure that the destination is of sufficient size to hold the character data to be copied and the NULLnull-termination character. |
Non-Compliant Code Example (off-by-1 error)
Wiki Markup |
---|
This non-compliant code example demonstrates what is commonly referred to as an _off-by-one_ error \[[Dowd 06|AA. C References#Dowd 06]\]. The loop copies data from {{src}} to {{dest}}. However, the NULLnull terminator may incorrectly be written one byte past the end of {{dest}}. The flaw exists because the loop does not account for the NULLnull-termination character that must be appended to {{dest}}. |
...
To correct this example, the terminating loop termination condition of the loop must be modified to account for the NULLnull-termination character that is appended to dest
.
...
Wiki Markup |
---|
Command-line arguments are passed to {{main()}} as pointers to NULLnull-terminated byte strings in the array members {{argv\[0\]}} through {{argv\[argc-1\]}}. If the value of {{argc}} is greater than zero, the string pointed to by {{argv\[0\]}} represents the program name. If the value of {{argc}} is greater than one, the strings pointed to by {{argv\[1\]}} through {{argv\[argc-1\]}} represent the program parameters. |
...
Remember to add a byte to accommodate the NULL-terminated byte string.
Non-Compliant Code Example ( argv
TOCTOU)
While the above example is secure, the more generic case where the source string is changeable is vulnerable to a TOCTOU race condition.
Code Block | ||
---|---|---|
| ||
char *copy_string(char const *src) {
/* ... */
char *dest = (char *)malloc(strlen(src)+1);
if (dest != NULL) {
strcpy(dest, src);
}
else {
/* Couldn't get the memory - recover */
}
/* ... */
}
|
If the memory at src
is modified between the call to malloc()
and the call to strcpy()
, the strcpy()
call can still produce a buffer overflow. Modification of the memory could easily occur in a multithreaded program. It could also occur in a single-threaded program if the src
pointer were out of bounds, and it pointed to memory directly modified by the malloc()
call.
Effectively, this problem arises because the length of the string is theoretically read twice, once by strlen()
to preallocate space, and once, implicitly, by strcpy()
, which continues to copy data until it encounters a '\0
' character.
To avoid this problem, use memcpy()
or strcpy_s()
as described belownull-terminated byte string.
Compliant Solution (argv
) (strcpy_s()
)
...
Code Block | ||
---|---|---|
| ||
int main(int argc, char *argv[]) { /* ... */ char * prog_name; size_t prog_size; prog_size = strlen(argv[0])+1; prog_name = (char *)malloc(prog_size); if (prog_name != NULL) { if (strcpy_s(prog_name, prog_size, argv[0])) { /* Handle strcpy_s() error */ } } } else { /* Couldn't get the memory - recover */ } /* ... */ } |
The strcpy_s()
function can also be used with a fixed-size to copy data to or from dynamically allocated memory or a statically allocated array. If insufficient space is available strcpy_s()
will return returns an error.
Compliant Solution (argv
) (memcpy()
)
The C standard memcpy()
function can provide the same functionality for this example as provide a similar capability to strcpy_s()
, but is more universally available.
Code Block | ||
---|---|---|
| ||
int main(int argc, char *argv[]) { /* ... */ char *prog_name; size_t prog_size; prog_size = strlen(argv[0])+1; prog_name = (char *)malloc(prog_size); if (prog_name != NULL) { memcpy(prog_name, argv[0], prog_size); } else { /* Couldn't get the memory - recover */ } /* ... */ } |
The memcpy()
function differs from strcpy_s()
in that it never returns an error. It always returns a pointer to the destination string (e.g., its first argument). However, memcpy()
does not validate that the destination pointer has enough space for the memory being copied, and it should not cannot be used if the source and destination strings overlap.
...
Environmental variables are loaded into process memory when the program is loaded. As a result, the length of these NULLnull-terminated byte strings can be determined by calling the strlen()
function and the resulting length used to allocate adequate dynamic memory:
...
Copying NTBS data to a buffer that is too small to hold that data results in a buffer overflow. Attackers can use exploit this condition to execute arbitrary code with the permissions of the vulnerable process.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
STR31-C | high | likely | medium | P18 | L1 |
...