Copying data to a buffer that is not large enough to hold that data results in a buffer overflow. Buffer overflows occur frequently when manipulating strings [Seacord 2013b]. 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 See STR03-C. Do not inadvertently truncate a string.).
When strings live on the heap, this rule is a specific instance of MEM35-C. Allocate sufficient memory for an object. Because strings are represented as arrays of characters, this rule is related to both ARR30-C. Do not form or use out-of-bounds pointers or array subscripts and ARR38-C. Guarantee that library functions do not form invalid pointers.
...
The gets_s()
function reads, at most, one less than the number of characters specified from the stream pointed to by stdin
into an array.
The C Standard, Annex K 3.5.4.1 paragraph 4 [ISO/IEC 9899:20112024], states:
No additional characters are read after a new-line character (which is discarded) or after end-of-file. The discarded new-line character does not count towards number of characters read. A null character is written immediately after the last character read into the array.
...
The getline()
function is similar to the fgets()
function but can dynamically allocate memory for the input buffer. If passed a null pointer, getline()
dynamically allocates a buffer of sufficient size to hold the input. If passed a pointer to dynamically allocated storage that is too small to hold the contents of the string, the getline()
function resizes the buffer, using realloc()
, rather than truncating the input. If successful, the getline()
function returns the number of characters read, which can be used to determine if the input has any null characters before the newline. The getline()
function works only with dynamically allocated buffers. Allocated memory must be explicitly deallocated by the caller to avoid memory leaks. (see See MEM31-C. Free dynamically allocated memory when no longer needed.).
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <stdlib.h> #include <string.h> void func(void) { int ch; size_t buffer_size = 32; char *buffer = malloc(buffer_size); if (!buffer) { /* Handle error */ return; } if ((ssize_t size = getline(&buffer, &buffer_size, stdin)) == -1) { /* Handle error */ } else { char *p = strchr(buffer, '\n'); if (p) { *p = '\0'; } else { /* Newline not found; flush stdin to end of line */ while ((ch = getchar()) != '\n' && ch != EOF) ; if (ch == EOF && !feof(stdin) && !ferror(stdin)) { /* Character resembles EOF; handle error */ } } } free (buffer); } |
Note that the getline()
function uses an in-band error indicator, in violation of ERR02-C. Avoid in-band error indicators.
Noncompliant Code Example (getchar()
)
...
In this compliant solution, characters are no longer copied to buf
once index == BUFFERSIZE - 1
, leaving room to null-terminate the string. The loop continues to read characters until the end of the line, the end of the file, or an error is encountered. When chars_read > index
truncated == true
, the input string has been truncated.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> enum { BUFFERSIZE = 32 }; void func(void) { char buf[BUFFERSIZE]; int ch; size_t index = 0; size_t chars_readbool truncated = 0false; while ((ch = getchar()) != '\n' && ch != EOF) { if (index < sizeof(buf) - 1) { buf[index++] = (char)ch; } else { chars_read++ truncated = true; } } buf[index] = '\0'; /* Terminate string */ if (ch == EOF) { /* Handle EOF or error */ } if (chars_read > indextruncated) { /* Handle truncation */ } } |
...
The strcpy_s()
function provides additional safeguards, including accepting the size of the destination buffer as an additional argument. (see See STR07-C. Use the bounds-checking interfaces for string manipulation.).
Code Block | ||||
---|---|---|---|---|
| ||||
#define __STDC_WANT_LIB_EXT1__ 1 #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { /* Ensure argv[0] is not 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; } |
...
According to the C Standard, 7.22.4.6 [ISO/IEC 9899:2011]:
The
getenv
function searches an environment list, provided by the host environment, for a string that matches the string pointed to byname
. The set of environment names and the method for altering the environment list are implementation defined.
...
In this noncompliant code 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.
...
Code Block | ||||
---|---|---|---|---|
| ||||
#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()
functionYou can also use *
to indicate that the precision should be provided as a variadic argument:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> void func(const char *name) { char filename[128]; snprintfsprintf(filename, "%.*s.txt", sizeof(filename), "%s.txt" - 5, name); } |
Risk Assessment
Copying string 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
...
LANG.MEM.BO
LANG.MEM.TO
MISC.MEM.NTERM
BADFUNC.BO.*
...
Buffer overrun
Type overrun
No space for null terminator
A collection of warning classes that report uses of library functions prone to internal buffer overflows
...
...
...
...
Can detect violations of the rule. However, it is unable to handle cases involving strcpy_s()
or manual string copies such as the one in the first example
...
STRING_OVERFLOW
STRING_SIZE
SECURE_CODING
...
Fully implemented
...
...
5.0
...
...
Compliant Solution (snprintf()
)
A more general solution is to use the snprintf()
function, which also truncates name
if it will not fit in the filename
.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h>
void func(const char *name) {
char filename[128];
int result = snprintf(filename, sizeof(filename), "%s.txt", name);
if (result != strlen(filename) {
/* truncation occurred */
}
}
|
Risk Assessment
Copying string 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
Array access out of bounds, Buffer overflow from incorrect string format specifier, Destination buffer overflow in string manipulation, Invalid use of standard library string routine, Missing null in string array, Pointer access out of bounds, Tainted NULL or non-null-terminated string, Use of dangerous standard function
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| Supported Astrée reports all buffer overflows resulting from copying data to a buffer that is not large enough to hold that data. | |||||||
Axivion Bauhaus Suite |
| CertC-STR31 | Detects calls to unsafe string function that may cause buffer overflow | ||||||
CodeSonar |
| LANG.MEM.BO | Buffer overrun | ||||||
Can detect violations of the rule. However, it is unable to handle cases involving | |||||||||
Coverity |
| STRING_OVERFLOW BUFFER_SIZE OVERRUN STRING_SIZE | Fully implemented | ||||||
5.0 | |||||||||
Helix QAC |
| C2840, C5009, C5038 C++0145, C++5009, C++5038 DF2840, DF2841, DF2842, DF2843, DF2845, DF2846, DF2847, DF2848, DF2930, DF2931, DF2932, DF2933, DF2935, DF2936, DF2937, DF2938 | |||||||
| SV.FMT_STR.BAD_SCAN_FORMAT | ||||||||
| 489 S, 109 D, 66 X, 70 X, 71 X | Partially implemented | |||||||
Parasoft C/C++test |
| CERT_C-STR31-a | Avoid accessing arrays out of bounds | ||||||
PC-lint Plus |
| 421, 498 | Partially supported | ||||||
Polyspace Bug Finder |
| Checks for:
Rule partially covered. | |||||||
PVS-Studio |
| V518, V645, V727, V755 | |||||||
| |||||||||
TrustInSoft Analyzer |
| mem_access | Exhaustively verified (see one compliant and one non-compliant example). |
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].
CVE-2009-0587 results from a violation of this rule. Before version 2.24.5, Evolution Data Server performed unchecked arithmetic operations on the length of a user-input string and used the value to allocate space for a new buffer. An attacker could thereby execute arbitrary code by inputting a long string, resulting in incorrect allocation and buffer overflow [xorl 2009].
CVE-2021-3156 results from a violation of this rule in versions of Sudo before 1.9.5p2. Due to inconsistencies on whether backslashes are escaped, vulnerable versions of Sudo enabled a user to create a heap-based buffer overflow and exploit it to execute arbitrary code. [BC].
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Key here (explains table format and definitions)
Taxonomy | Taxonomy item | Relationship |
---|---|---|
CERT C Secure Coding Standard | STR03-C. Do not inadvertently truncate a string | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT C Secure Coding Standard | STR07-C. Use the bounds-checking interfaces for remediation of existing string manipulation code MSC24-C. Do not use deprecated or obsolescent functions MEM00-C. Allocate and free memory in the same module, at the same level of abstraction FIO34-C. Distinguish between characters read from a file and EOF or WEOF | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TR 24772:2013 | String Termination [CJM] | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TR 24772:2013 | Buffer Boundary Violation (Buffer Overflow) [HCB] | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TR 24772:2013 | Unchecked Array Copying [XYW] | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TS 17961:2013 | Using a tainted value to write to an object using a formatted input or output function [taintformatio] | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TS 17961:2013 | Tainted strings are passed to a string copying function [taintstrcpy] | Prior to 2018-01-12: CERT: Unspecified Relationship |
CWE 2.11 | CWE-119, Improper Restriction of Operations within the Bounds of a Memory Buffer | 2017-05-18: CERT: Rule subset of CWE |
CWE 2.11 | CWE-120, Buffer Copy without Checking Size of Input ("Classic Buffer Overflow") | 2017-05-15: CERT: Exact |
CWE 2.11 | CWE-123, Write-what-where Condition | 2017-06-12: CERT: Partial overlap |
CWE 2.11 | CWE-125, Out-of-bounds Read | 2017-05-18: CERT: Partial overlap |
CWE 2.11 | CWE-676, Off-by-one Error | 2017-05-18: CERT: Partial overlap |
CERT-CWE Mapping Notes
Key here for mapping notes
CWE-122 and STR31-C
STR31-C = Union( CWE-122, list) where list =
- Buffer overflows on strings in the stack or data segment
CWE-125 and STR31-C
Independent( ARR30-C, ARR38-C, EXP39-C, INT30-C)
STR31-C = Subset( Union( ARR30-C, ARR38-C))
STR32-C = Subset( ARR38-C)
Intersection( STR31-C, CWE-125) =
- Directly reading beyond the end of a string
STR31-C – CWE-125 =
- Directly writing beyond the end of a string
CWE-125 – STR31-C =
- Reading beyond a non-string array
- Reading beyond a string using library functions
CWE-676 and STR31-C
- Independent( ENV33-C, CON33-C, STR31-C, EXP33-C, MSC30-C, ERR34-C)
- STR31-C implies that several C string copy functions, like strcpy() are dangerous.
Intersection( CWE-676, STR31-C) =
- Buffer Overflow resulting from invocation of the following dangerous functions:
- gets(), fscanf(), strcpy(), sprintf()
STR31-C – CWE-676 =
- Buffer overflow that does not involve the dangerous functions listed above.
CWE-676 - STR31-C =
- Invocation of other dangerous functions
CWE-121 and STR31-C
STR31-C = Union( CWE-121, list) where list =
- Buffer overflows on strings in the heap or data segment
CWE-123 and STR31-C
Independent(ARR30-C, ARR38-C)
STR31-C = Subset( Union( ARR30-C, ARR38-C))
STR32-C = Subset( ARR38-C)
Intersection( CWE-123, STR31-C) =
- Buffer overflow that overwrites a (unrelated) pointer with untrusted data
STR31-C – CWE-123 =
- Buffer overflow that does not overwrite a (unrelated) pointer
CWE-123 – STR31-C =
- Arbitrary writes that do not involve buffer overflows
CWE-119 and STR31-C
Independent( ARR30-C, ARR38-C, ARR32-C, INT30-C, INT31-C, EXP39-C, EXP33-C, FIO37-C)
STR31-C = Subset( Union( ARR30-C, ARR38-C))
STR32-C = Subset( ARR38-C)
CWE-119 = Union( STR31-C, list) where list =
- Out-of-bounds reads or writes that are not created by string copy operations
CWE-193 and STR31-C
Intersection( CWE-193, STR31-C) = Ø
CWE-193 involves an integer computation error (typically off-by-one), which is often a precursor to (slight) buffer overflow. However the two errors occur in different operations and are thus unrelated.
...
...
NNTS.TAINTED
SV.STRBO.GETS
SV.USAGERULES.UNBOUNDED_STRING_COPY
...
...
...
489 S, 109 D, 66 X, 70 X, 71 X
...
Partially implemented
...
...
...
...
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].
CVE-2009-0587 results from a violation of this rule. Before version 2.24.5, Evolution Data Server performed unchecked arithmetic operations on the length of a user-input string and used the value to allocate space for a new buffer. An attacker could thereby execute arbitrary code by inputting a long string, resulting in incorrect allocation and buffer overflow [xorl 2009].
Search for additional vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
...
STR03-C. Do not inadvertently truncate a string
STR07-C. Use the bounds-checking interfaces for remediation of existing string manipulation code
MSC24-C. Do not use deprecated or obsolescent functions
MEM00-C. Allocate and free memory in the same module, at the same level of abstraction
FIO34-C. Distinguish between characters read from a file and EOF or WEOF
...
Using a tainted value to write to an object using a formatted input or output function [taintformatio]
Tainted strings are passed to a string copying function [taintstrcpy]
...
Bibliography
[Dowd 2006] | Chapter 7, "Program Building Blocks" ("Loop Constructs," pp. 327–336) |
[Drepper 2006] | Section 2.1.1, "Respecting Memory Bounds" |
[ISO/IEC 9899: |
2024] | K.3.5.4.1, "The gets_s Function" |
[Lai 2006] |
[NIST 2006] | SAMATE Reference Dataset Test Case ID 000-000-088 |
[Seacord 2013b] | Chapter 2, "Strings" |
[xorl 2009] | FreeBSD-SA-09:11: NTPd Remote Stack Based Buffer Overflows |
...
...