...
Wiki Markup |
---|
This noncompliant 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}} 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';
/* ... */
|
...
To correct this example, the loop termination condition 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';
/* ... */
|
...
Arguments read from the command line are 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 |
---|
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 noncompliant 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]);
/* ... */
}
|
...
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 */
}
/* ... */
}
|
...
The strcpy_s()
function provides additional safeguards, including accepting the size of the destination buffer as an additional argument (see STR07-C. 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 C standard memcpy()
function provide a similar capability to strcpy_s()
, but is 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 */
}
/* ... */
}
|
...
If an argument is not going to be modified or concatenated, there is no reason to make a copy of the string. Not copying a string is the best way to prevent a buffer overflow, and is also the most efficient solution.
Code Block |
---|
|
int main(int argc, char *argv[]) {
/* ... */
const char *progname = argv[0];
size_t prog_size;
/* ... */
}
|
...
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];
char *editor = getenv("EDITOR");
if (editor == NULL) {
/* EDITOR environment variable not set */
} else {
strcpy(buff, editor);
}
/* ... */
|
...
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 *buff;
char *editor = getenv("EDITOR");
if (editor == NULL) {
/* EDITOR environment variable not set */
} else {
size_t len = strlen(editor)+1;
buff = (char *)malloc(len);
if (buff == NULL) {
/* Handle malloc() Error */
}
memcpy(buff, editor, len);
}
/* ... */
|
...
In this example, name
refers to an external string; it could have originated from user input, or from the file system, or from the network. The program constructs a filename from the string in preparation for opening the file.
Code Block |
---|
|
char* name; /* initialized externally */
char filename[128];
sprintf( filename, "%s.txt", name);
/* open filename * /
|
...
The buffer overflow can be prevented by providing a precision length to the %s
specifier. The value 123 ensures that filename can contain the first 123 characters of name
, the ".txt" extension, and the null terminator.
Code Block |
---|
|
char* name; /* initialized externally */
char filename[128];
sprintf( filename, "%.123s.txt", name);
/* open filename * /
|
...
A more general solution is to use the snprintf()
function.
Code Block |
---|
|
char* name; /* initialized externally */
char filename[128];
snprintf( filename, sizeof( filename), "%s.txt", name);
/* open filename * /
|
...
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
1.) An sprintf vulnerability existed in the FreeBSD implementation of the Network Time Protocol (NTPd) before versions 4.2.4p7 and 4.2.5p74. The code was fixed by using snprintf instead. Details can be found at the National Vulnerability Database and this blog post.
Other Languages
This rule appears in the C++ Secure Coding Standard as STR31-CPP. Guarantee that storage for character arrays has sufficient space for character data and the null terminator.
...