Copying data to a buffer that is not large enough to hold that data results in a buffer overflow. Buffer overflows are not limited to null-terminated byte strings (NTBS), but they often occur when manipulating NTBS data. 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 null-terminated byte string.)
Noncompliant Code Example (Off-by-One Error)
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
.
enum { ARRAY_SIZE = 32 }; void func(void) { 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-One Error)
To correct this example, the loop termination condition must be modified to account for the null-termination character that is appended to dest
:
enum { ARRAY_SIZE = 32 }; void func(void) { 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'; }
Noncompliant Code Example (argv
)
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:
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[argc - 1]
. If the value of argc
is greater than 0, the string pointed to by argv[0]
is, by convention, the program name. If the value of argc
is greater than 1, the strings referenced by argv[1]
through argv[argc - 1]
are the 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 argv[0]
can be manipulated by an attacker to cause a buffer overflow:
#include <string.h> int main(int argc, char *argv[]) { char prog_name[128]; strcpy(prog_name, argv[0]); return 0; }
Compliant Solution (argv
)
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.
#include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { /* Be prepared for argv[0] to be null */ const char *const name = (argc && argv[0]) ? argv[0] : ""; char *prog_name = (char *)malloc(strlen(name) + 1); if (prog_name != NULL) { strcpy(prog_name, name); } else { /* Handle error */ } free(prog_name); return 0; }
Remember to add a byte to accommodate the null-terminated byte string.
Compliant Solution (Annex K, argv
)
The strcpy_s()
function provides additional safeguards, including accepting the size of the destination buffer as an additional argument. (See STR07-C. Use the bounds-checking interfaces for remediation of existing string manipulation code.) Do not assume that argv[0]
is non-null.
#define __STDC_WANT_LIB_EXT1__ 1 #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { /* Be prepared for argv[0] to be null */ const char *const name = (argc && 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 error */ } } else { /* Handle error */ } free(prog_name); return 0; }
The strcpy_s()
function can be used to copy data to or from dynamically allocated memory or a statically allocated array. If insufficient space is available, strcpy_s()
returns an error.
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. Care must be taken to avoid assuming that argv[0]
is non-null.
int main(int argc, char *argv[]) { /* Be prepared for argv[0] to be null */ const char * const prog_name = (argc && argv[0]) ? argv[0] : ""; return 0; }
Noncompliant Code Example (getenv()
)
The getenv()
function searches the 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.
#include <stdlib.h> #include <string.h> void func(void) { char buff[256]; char *editor = getenv("EDITOR"); if (editor == NULL) { /* EDITOR environment variable not set */ } else { strcpy(buff, editor); } }
Compliant Solution (getenv()
)
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 can be used to allocate adequate dynamic memory:
#include <stdlib.h> #include <string.h> void func(void) { 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 error */ } memcpy(buff, editor, len); free(buff); } }
Noncompliant Code Example (sprintf()
)
In this example, name
refers to an external string; it could have originated from user input, from the file system, or from the network. The program constructs a file name from the string in preparation for opening the file.
#include <stdio.h> void func(const char *name) { char filename[128]; sprintf(filename, "%s.txt", name); }
However, because the sprintf()
function makes no guarantees regarding the length of the string generated, a sufficiently long string in name
could generate a buffer overflow.
Compliant Solution (sprintf()
)
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.
#include <stdio.h> void func(const char *name) { char filename[128]; sprintf(filename, "%.123s.txt", name); }
Compliant Solution (snprintf()
)
A more general solution is to use the snprintf()
function:
#include <stdio.h> void func(const char *name) { char filename[128]; snprintf(filename, sizeof(filename), "%s.txt", name); }
Risk Assessment
Copying NTBS data to a buffer that is too small to hold that data results in a buffer overflow. Attackers can 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 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
|
| Can detect violations of the rule. However, it is unable to handle cases involving | |
Coverity | 6.5 | STRING_OVERFLOW STRING_SIZE | Fully Implemented Fully implemented |
5.0 |
|
| |
2024.3 |
|
| |
9.7.1 |
|
| |
3.1.1 |
|
|
Related Vulnerabilities
CVE-2009-1252 results from a violation of this rule. The Network Time Protocol daemon (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].
Search for additional vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
CERT C Secure Coding Standard | STR03-C. Do not inadvertently truncate a null-terminated byte string STR07-C. Use the bounds-checking interfaces for remediation of existing string manipulation code |
CERT C++ Secure Coding Standard | STR31-CPP. Guarantee that storage for character arrays has sufficient space for character data and the null terminator |
ISO/IEC TR 24772:2013 | String Termination [CJM] Buffer Boundary Violation (Buffer Overflow) [HCB] Unchecked Array Copying [XYW] |
ISO/IEC TS 17961 | Using a tainted value to write to an object using a formatted input or output function [taintformatio] |
MITRE CWE | CWE-119, Failure to constrain operations within the bounds of an allocated memory buffer CWE-120, Buffer copy without checking size of input ("classic buffer overflow") CWE-193, Off-by-one error |
Bibliography
[Dowd 2006] | Chapter 7, "Program Building Blocks" ("Loop Constructs," pp. 327–336) |
[Seacord 2013] | Chapter 2, "Strings" |
[xorl 2009] | FreeBSD-SA-09:11: NTPd Remote Stack Based Buffer Overflows |