Strings must contain a null-termination character at or before the address of the last element of the array before they can be safely passed as arguments to standard string-handling functions, such as strcpy()
or strlen()
. This is because these functions, as well as other string-handling functions defined by the C Standard [ISO/IEC 9899:2011], depend on the existence of a null-termination character to determine the length of a string. Similarly, strings must be null-terminated before iterating on a character array where the termination condition of the loop depends on the existence of a null-termination character within the memory allocated for the string, as in the following example:
Code Block | ||
---|---|---|
| ||
size_t i; void func(void) { char ntbs[16]; /* ... */ for (size_t i = 0; i < sizeof(ntbs); ++i) { if (ntbs[i] == '\0') { break; /* ... */} } |
Failure to properly terminate null-terminated byte strings can result in buffer overflows and other undefined behavior.
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <string.h> enum { NTBS_SIZE = 32 }; void func(void) { char ntbs[NTBS_SIZE]; ntbs[sizeof(ntbs) - 1] = '\0'; strncpy(ntbs, source, sizeof(ntbs)); } |
In the second noncompliant code example, memset()
is used to clear the destination buffer; unfortunately, the third argument incorrectly specifies the size of the destination array [Schwarz 2005]:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <string.h> enum { NTBS_SIZE = 32 }; void func(void) { char ntbs[NTBS_SIZE]; memset(ntbs, 0, sizeof(ntbs) - 1); strncpy(ntbs, source, sizeof(ntbs) - 1); } |
Compliant Solution (Truncation)
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <string.h> enum { NTBS_SIZE = 32 }; void func(void) { char ntbs[NTBS_SIZE]; strncpy(ntbs, source, sizeof(ntbs) - 1); ntbs[sizeof(ntbs) - 1] = '\0'; } |
Compliant Solution (Copy without Truncation)
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <string.h> enum { NTBS_SIZE = 32 }; void func(void) { char *source = "0123456789abcdef"; char ntbs[NTBS_SIZE]; /* ... */ if (source) { if (strlen(source) < sizeof(ntbs)) { strcpy(ntbs, source); } else { /* Handle string too large condition. */ } } else { /* Handle NULL string condition. */ } } |
Compliant Solution (strncpy_s(),
C11 Annex K)
...
Code Block | ||||
---|---|---|---|---|
| ||||
#define __STDC_WANT_LIB_EXT1__ #include <string.h> enum { NTBS_SIZE = 32 }; void func(void) { char *source = "0123456789abcdef"; char a[NTBS_SIZE]; /* ... */ if (source) { errno_t err = strncpy_s(a, sizeof(a), source, 5); if (err != 0) { /* Handle error */ } } else { /* Handle NULL string condition. */ } } |
Noncompliant Code Example (realloc()
)
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> char *cur_msg = NULL; size_t cur_msg_size = 1024; /* ... */ void lessen_memory_usage(void) { char *temp; size_t temp_size; /* ... */ if (cur_msg != NULL) { temp_size = cur_msg_size / 2 + 1; temp = realloc(cur_msg, temp_size); if (temp == NULL) { /* Handle error condition */ } cur_msg = temp; cur_msg_size = temp_size; } } /* ... */ |
Because realloc()
does not guarantee that the string is properly null-terminated, any subsequent operation on cur_msg
that assumes a null-termination character may result in undefined behavior.
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> char *cur_msg = NULL; size_t cur_msg_size = 1024; /* ... */ void lessen_memory_usage(void) { char *temp; size_t temp_size; /* ... */ if (cur_msg != NULL) { temp_size = cur_msg_size / 2 + 1; temp = realloc(cur_msg, temp_size); if (temp == NULL) { /* Handle error condition */ } cur_msg = temp; cur_msg_size = temp_size; /* Ensure string is null-terminated. */ cur_msg[cur_msg_size - 1] = '\0'; } } /* ... */ |
Risk Assessment
Failure to properly null-terminate strings 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.
...
CERT C++ Secure Coding Standard | STR32-CPP. Null-terminate character arrays as required | ISO/IEC 9899:2011 | K.7.1.4, "The|
ISO/IEC TR 24772:2013 | String Termination [CMJ] | ||
ISO/IEC TS 17961 (Draft) | 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
[ISO/IEC 9899:2011] | Subclause K.7.1.4, "The strncpy_s function" |
[Schwarz 2005] | |
[Seacord 2013] | Chapter 2, "Strings" |
[Viega 2005] | Section 5.2.14, "Miscalculated NULL Termination" |
...