According to the C Standard, 6.4.5, paragraph 3 [ISO/IEC 9899:2011]:
A character string literal is a sequence of zero or more multibyte characters enclosed in double-quotes, as in
"xyz"
. A UTF−8 string literal is the same, except prefixed byu8
. A wide string literal is the same, except prefixed by the letterL
,u
, orU
.
At compile time, string literals are used to create an array of static storage duration of sufficient length to contain the character sequence and a terminating null character. String literals are usually referred to by a pointer to (or array of) characters. Ideally, they should be assigned only to pointers to (or arrays of) const char
or const wchar_t
. It is unspecified whether these arrays of string literals are distinct from each other. The behavior is undefined if a program attempts to modify any portion of a string literal. Modifying a string literal frequently results in an access violation because string literals are typically stored in read-only memory (see undefined behavior 33.)
Avoid assigning a string literal to a pointer to non-const
or casting a string literal to a pointer to non-const
. For the purposes of this rule, a pointer to (or array of) const
characters must be treated as a string literal. Similarly, the returned value of the following library functions must be treated as a string literal if the first argument is a string literal:
strpbrk(), strchr(), strrchr(), strstr()
wcspbrk(), wcschr(), wcsrchr(), wcsstr()
memchr(), wmemchr()
This rule is a specific instance of EXP40-C. Do not modify constant objects.
Noncompliant Code Example
In this noncompliant code example, the char
pointer p
is initialized to the address of a string literal. Attempting to modify the string literal is undefined behavior:
char *p = "string literal"; p[0] = 'S';
Compliant Solution
As an array initializer, a string literal specifies the initial values of characters in an array as well as the size of the array (see STR11-C. Do not specify the bound of a character array initialized with a string literal). This code creates a copy of the string literal in the space allocated to the character array a
. The string stored in a
can be modified safely.
char a[] = "string literal"; a[0] = 'S';
Noncompliant Code Example (POSIX)
In this noncompliant code example, a string literal is passed to the (pointer to non-const
) parameter of the POSIX function mkstemp()
, which then modifies the characters of the string literal:
#include <stdlib.h> void func(void) { mkstemp("/tmp/edXXXXXX"); }
The behavior of mkstemp()
is described in more detail in FIO21-C. Do not create temporary files in shared directories.
Compliant Solution (POSIX)
This compliant solution uses a named array instead of passing a string literal:
#include <stdlib.h> void func(void) { static char fname[] = "/tmp/edXXXXXX"; mkstemp(fname); }
Noncompliant Code Example (Result of strrchr()
)
In this noncompliant example, the char *
result of the strrchr()
function is used to modify the object pointed to by pathname
. Because the argument to strrchr()
points to a string literal, the effects of the modification are undefined.
#include <stdio.h> #include <string.h> const char *get_dirname(const char *pathname) { char *slash; slash = strrchr(pathname, '/'); if (slash) { *slash = '\0'; /* Undefined behavior */ } return pathname; } int main(void) { puts(get_dirname(__FILE__)); return 0; }
Compliant Solution (Result of strrchr()
)
This compliant solution avoids modifying a const
object, even if it is possible to obtain a non-const
pointer to such an object by calling a standard C library function, such as strrchr()
. To reduce the risk to callers of get_dirname()
, a buffer and length for the directory name are passed into the function. It is insufficient to change pathname
to require a char *
instead of a const char *
because conforming compilers are not required to diagnose passing a string literal to a function accepting a char *
.
#include <stddef.h> #include <stdio.h> #include <string.h> char *get_dirname(const char *pathname, char *dirname, size_t size) { const char *slash; slash = strrchr(pathname, '/'); if (slash) { ptrdiff_t slash_idx = slash - pathname; if ((size_t)slash_idx < size) { memcpy(dirname, pathname, slash_idx); dirname[slash_idx] = '\0'; return dirname; } } return 0; } int main(void) { char dirname[260]; if (get_dirname(__FILE__, dirname, sizeof(dirname))) { puts(dirname); } return 0; }
Risk Assessment
Modifying string literals can lead to abnormal program termination and possibly denial-of-service attacks.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
STR30-C | Low | Likely | Low | P9 | L2 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Can detect simple violations of this rule | |||
9.7.1 | 157 S | Partially implemented | |
PRQA QA-C | Unable to render {include} The included page could not be found. | 0556 | Partially implemented |
3.1.1 |
|
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Bibliography
[ISO/IEC 9899:2011] | 6.4.5, "String Literals" |
[Plum 1991] | Topic 1.26, "Strings—String Literals" |
[Summit 1995] | comp.lang.c FAQ List, Question 1.32 |