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 null-terminated byte strings (NTBS), this type of 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 null-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 null termination character. |
...
Non-Compliant Code Example (off-by-1
...
)
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 null terminator may incorrectly be written one byte past the end of {{dest}}. The flaw exists because the loop does not account for the null termination character that must be appended to {{dest}}. |
Code Block |
---|
|
char dest[ARRAY_SIZE];
char src[ARRAY_SIZE];
size_t i;
/* ... */
for (i=0; src[i] && (i < sizeof(dest)); i++) {
dest[i] = src[i];
}
dest[i] = '\0';
/* ... */
|
Compliant Solution (
...
off-by-1
...
)
To correct this example, the terminating condition of the loop must be modified to account for the null termination character that is appended to dest
.
Code Block |
---|
|
char dest[ARRAY_SIZE];
char src[ARRAY_SIZE];
size_t i;
/* ... */
for (i=0; src[i] && (i < sizeof(dest)-1); i++) {
dest[i] = src[i];
}
dest[i] = '\0';
/* ... */
|
Non-Compliant Code Example ( strcpy()
)
Arguments read from the command line and stored in process memory. The function main()
, called at program startup, is typically declared as follows when the program accepts command line arguments:
Code Block |
---|
|
int main(int argc, char *argv[]) { /* ... */ }
|
Wiki Markup |
---|
Command line arguments are passed to {{main()}} as pointers to null-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. |
Wiki Markup |
---|
The parameters {{argc}} and {{argv}} and the strings pointed to by the {{argv}} array are not modifiable by the program, and retain their last-stored values between program startup and program termination. This requires that a copy of these parameters be made before the strings can be modified. Vulnerabilities can occur when inadequate space is allocated to copy a command line argument. In this non-compliant code example, the contents of {{argv\[0\]}} can be manipulated by an attacker to cause a buffer overflow: |
Code Block |
---|
|
int main(int argc, char *argv[]) {
/* ... */
char prog_name[128];
strcpy(prog_name, argv[0]);
/* ... */
}
|
Compliant Solution ( strcpy()
)
Wiki Markup |
---|
The {{strlen()}} function can be used to determine the length of the strings referenced by {{argv\[0\]}} through {{argv\[argc-1\]}} so that adequate memory can be dynamically allocated: |
Code Block |
---|
|
int main(int argc, char *argv[]) {
/* ... */
char * prog_name = (char *)malloc(strlen(argv[0])+1);
if (prog_name != NULL) {
strcpy(prog_name, argv[0]);
}
else {
/* Couldn't get the memory - recover */
}
/* ... */
}
|
Remember to add a byte to accommodate the null-terminated byte string.
Non-Compliant Code Example ( strcpy()
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 |
---|
|
cchar* copy_string(char* 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 below.
Compliant Solution ( strcpy_s()
)
Wiki Markup |
---|
The {{strcpy_s()}} function provides additional safeguards, including accepting the size of the destination buffer as an additional argument \[[STR00-A. Use TR 24731 for remediation of existing string manipulation code]\]. |
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 statically allocated array. If insufficient space is available strcpy_s()
will return an error.
Compliant Solution ( memcpy()
)
The memcpy()
function operates in principle just like 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 be used if the source and destination strings overlap.
Non-Compliant Code Example ( getenv()
)
The getenv()
function searches an environment list, provided by the host environment, for a string that matches the string pointed to by name. The set of environment names and the method for altering the environment list are implementation-defined. Environment variables can be arbitrarily large, and copying them into fixed length arrays without first determining the size and allocating adequate storage can result in a buffer overflow.
Code Block |
---|
|
/* ... */
char buff[256];
strcpy(buff, getenv("EDITOR"));
/* ... */
|
Compliant Solution
Environmental variables are loaded into process memory when the program is loaded. As a result, the length of these null-terminated byte strings can be determined by calling the strlen()
function and the resulting length used to allocate adequate dynamic memory:
Code Block |
---|
|
/* ... */
char *editor;
char *buff;
editor = getenv("EDITOR");
if (editor) {
size_t len = strlen(editor)+1;
buff = (char *)malloc(len);
if (!buff) {
/* Handle malloc() Error */
}
memcpy(buff, editor, len);
}
/* ... */
|
...
getenv()
...
Risk Assessment
Copying NTBS data to a buffer that is too small to hold that data results in a buffer overflow. Attackers can use this to execute arbitrary code.
...