...
Noncompliant Code Example (Off-by-One Error)
This noncompliant code example demonstrates what is commonly referred to as an _off-by-one_ error \ [[Dowd 2006|AA. Bibliography#Dowd 06]\]. The loop copies data from {{ Wiki Markup 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'; /* ... */ |
...
Code Block | ||||
---|---|---|---|---|
| ||||
int main(int argc, char *argv[]) { /* ... */ } |
...
Command-line arguments are passed to {{main()
}} as pointers to null-terminated byte strings in the array members {{argv
\[0
\]}} through {{argv\]
through argv[argc-1
\]
}}. If the value of {{argc
}} is greater than zero, the string pointed to by {{argv
\[0
\]
}} is, by convention, the program name. If the value of {{argc
}} is greater than one, the strings referenced by {{argv\argv[1
\]
}} through {{argv
\[argc-1
\]
}} are the actual program arguments.
Vulnerabilities can occur when inadequate space is allocated to copy a command-line argument or other program input. In this noncompliant code example, the contents of {{ Wiki Markup 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 (argv
) (strcpy()
)
...
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. Note that care must be taken to avoid assuming that {{argv
\[0
\]
}} is non-null.
Code Block | ||||
---|---|---|---|---|
| ||||
int main(int argc, char *argv[]) { /* Be prepared for argv[0] to be null */ const char* const name = argv[0] ? argv[0] : ""; char *prog_name = (char *)malloc(strlen(name) + 1); if (prog_name != NULL) { strcpy(prog_name, name); } else { /* Failed to allocate memory - recover */ } /* ... */ } |
...
Compliant Solution (argv
) (strcpy_s()
)
...
The {{strcpy_s()
}} function provides additional safeguards, including accepting the size of the destination buffer as an additional argument. (See recommendation [STR07-C. Use the bounds-checking interfaces for remediation of existing string manipulation code].) Do not assume that {{argv
\[0
\]
}} is non-null.
Code Block | ||||
---|---|---|---|---|
| ||||
int main(int argc, char *argv[]) { /* Be prepared for argv[0] to be null */ const char* const name = argv[0] ? argv[0] : ""; char * prog_name; size_t prog_size; prog_size = strlen(name) + 1; prog_name = (char *)malloc(prog_size); if (prog_name != NULL) { if (strcpy_s(prog_name, prog_size, name)) { /* Handle strcpy_s() error */ } } else { /* Failed to allocate memory - recover */ } /* ... */ } |
...
Compliant Solution (argv
) (memcpy()
)
...
The C standard {{memcpy()
}} function provide a similar capability to {{strcpy_s()
}}, but is universally available. Note that care must be taken to avoid assuming that {{argv
\[0
\]
}} is non-null. Note also that {{memcpy
}} must not be called with a null pointer even when the second (size) argument is zero.
Code Block | ||||
---|---|---|---|---|
| ||||
int main(int argc, char *argv[]) { /* Be prepared for argv[0] to be null */ const char* const name = argv[0] ? argv[0] : ""; char *prog_name; size_t prog_size; prog_size = strlen(name) + 1; prog_name = (char *)malloc(prog_size); if (prog_name != NULL) { memcpy(prog_name, name, prog_size); } else { /* Failed to allocate memory - recover */ } /* ... */ } |
...
Compliant Solution (argv
)
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. Note that care must be taken to avoid assuming that {{ Wiki Markup argv
\[0
\]
}} is non-null.
Code Block | ||||
---|---|---|---|---|
| ||||
int main(int argc, char *argv[]) { /* Be prepared for argv[0] to be null */ const char *prog_name = argv[0] ? argv[0] : ""; size_t prog_size; /* ... */ } |
...
Tool | Version | Checker | Description | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
| ||||||||
|
|
|
| ||||||||
|
|
|
| ||||||||
|
|
|
| ||||||||
|
|
|
|
Related Vulnerabilities
...
[CVE-2009-1252|http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2009-1252] results from a violation of this rule. The Network Time Protocol results from a violation of this rule. The Network Time Protocol (NTPd), before versions 4.2.4p7 and 4.2.5p74, contained calls to sprintf that allow an attacker to execute arbitrary code by overflowing a character array \[ [xorl 2009|http://xorl.wordpress.com/ 2009/06/10/freebsd-sa-0911-ntpd-remote-stack-based-buffer-overflows/]\].
Search for additional vulnerabilities resulting from the violation of this rule on the CERT website.
...
MITRE CWE: CWE-193, "Off-by-one Error"
Bibliography
...
\[[Dowd 2006|AA. Bibliography#Dowd 06]\] Chapter ] Chapter 7, "Program Building Blocks" (Loop Constructs 327-336)
\[
[Seacord 2005a|AA. Bibliography#Seacord 05]\] Chapter 2, "Strings"
\
[[xorl 2009|AA. Bibliography#xorl 2009] \] ["FreeBSD-SA-09:11: NTPd Remote Stack Based Buffer Overflows"|http://xorl.wordpress.com/2009/06/10/freebsd-sa-0911-ntpd-remote-stack-based-buffer-overflows/]
...
07. Characters and Strings (STR) STR32-C. Null-terminate byte strings as required