Many library functions accept a string or wide string argument with the constraint that the string they receive is properly null-terminated. Passing a character sequence or wide character sequence that is not null-terminated to such a function can result in accessing memory that is outside the bounds of the object. Do not pass a character sequence or wide character sequence that is not null-terminated to a library function that expects a string or wide string argument.
Noncompliant Code Example
This code example is noncompliant because the character sequence str
will not be null-terminated when passed as an argument to printf()
. See STR11-C. Do not specify the bound of a character array initialized with a string literal.
char str[3] = "abc"; printf("%s\n", str);
Compliant Solution
This compliant solution does not specify the bound of a character array in the array declaration. If the array bound is omitted, the compiler allocates sufficient storage to store the entire string literal, including the terminating null character.
char str[3] = "abc"; printf("%s\n", str);
Noncompliant Code Example
This code example is noncompliant because the wide character sequence cur_msg
will not be null-terminated when passed to wcslen()
. This will occur if lessen_memory_usage()
is invoked while cur_msg_size
still has its initial value of 1024.
wchar_t *cur_msg = NULL; size_t cur_msg_size = 1024; size_t cur_msg_len = 0; void lessen_memory_usage(void) { wchar_t *temp; size_t temp_size; /* ... */ if (cur_msg != NULL) { temp_size = cur_msg_size / 2 + 1; temp = realloc(cur_msg, temp_size * sizeof(wchar_t)); // temp & cur_msg might not be null-terminated if (temp == NULL) { /* Handle error */ } cur_msg = temp; cur_msg_size = temp_size; cur_msg_len = wcslen(cur_msg); // error } }
Compliant Solution
In this compliant solution, cur_msg
will always be null-terminated when passed to wcslen()
.
wchar_t *cur_msg = NULL; size_t cur_msg_size = 1024; size_t cur_msg_len = 0; void lessen_memory_usage(void) { wchar_t *temp; size_t temp_size; /* ... */ if (cur_msg != NULL) { temp_size = cur_msg_size / 2 + 1; temp = realloc(cur_msg, temp_size * sizeof(wchar_t)); // temp & cur_msg might not be null-terminated if (temp == NULL) { /* Handle error */ } cur_msg = temp; // cur_msg now properly null-terminated cur_msg[temp_size - 1] = L'\0'; cur_msg_size = temp_size; cur_msg_len = wcslen(cur_msg); } }
Noncompliant Code Example (strncpy()
)
While the strncpy()
function takes a string as input, it does not guarantee that the resulting value is still null-terminated. In the following noncompliant code example, if no null character is contained in the first n
characters of the source
array, the result will not be null-terminated. Passing a non-null-terminated character sequence to strlen()
results in undefined behavior:
#include <string.h> enum { STR_SIZE = 32 }; size_t func(const char *source) { char str[STR_SIZE]; str[sizeof(str) - 1] = '\0'; strncpy(str, source, sizeof(str)); return strlen(str); }
Compliant Solution (Truncation)
The correct solution depends on the programmer's intent. If the intent is to truncate a string, this solution can be used:
#include <string.h> enum { STR_SIZE = 32 }; size_t func(const char *source) { char str[STR_SIZE]; strncpy(str, source, sizeof(str) - 1); str[sizeof(str) - 1] = '\0'; return strlen( str); }
Compliant Solution (Copy without Truncation)
If the intent is to copy without truncation, this example copies the data and guarantees that the resulting array is null-terminated. If the string cannot be copied, it is handled as an error condition.
#include <string.h> enum { STR_SIZE = 32 }; size_t func(const char *source) { char str[STR_SIZE]; if (source) { if (strlen(source) < sizeof(str)) { strcpy(str, source); } else { /* Handle string-too-large */ } } else { /* Handle null pointer */ } return strlen(str); }
Compliant Solution (strncpy_s(),
C11 Annex K)
The C11 Annex K strncpy_s()
function copies up to n
characters from the source array to a destination array. If no null character was copied from the source array, then the n
th position in the destination array is set to a null character, guaranteeing that the resulting string is null-terminated.
#define __STDC_WANT_LIB_EXT1__ 1 #include <string.h> enum { STR_SIZE = 32 }; size_t func(const char *source) { char a[STR_SIZE]; if (source) { errno_t err = strncpy_s(a, sizeof(a), source, 5); if (err != 0) { /* Handle error */ } } else { /* Handle null pointer */ } return strlen_s(s, sizeof(a)); }
Risk Assessment
Failure to properly null-terminate a character sequence that is passed to a library function that expects a string can result in buffer overflows and the execution of arbitrary code with the permissions of the vulnerable process. Null-termination errors can also result in unintended information disclosure.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
STR32-C | High | Probable | Medium | P12 | L1 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
|
| Can detect some violations of this rule | |
Coverity | 6.5 | STRING_NULL | Fully Implemented |
2024.3 | NNTS |
| |
9.7.1 | 600 S | Fully implemented |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
CERT C++ Secure Coding Standard | STR32-CPP. Null-terminate character arrays as required |
ISO/IEC TR 24772:2013 | String Termination [CMJ] |
ISO/IEC TS 17961 | Passing a non-null-terminated character sequence to a library function that expects a string [strmod] |
MITRE CWE | CWE-119, Failure to constrain operations within the bounds of an allocated memory buffer CWE-170, Improper null termination |
Bibliography
[Seacord 2013] | Chapter 2, "Strings" |
[Viega 2005] | Section 5.2.14, "Miscalculated NULL Termination" |