According to the C Standard, 6.4.5, paragraph 3 [ISO/IEC 9899:2024]:
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 by
...
u8. A wchar_t string literal is the same, except prefixed by L. A UTF-16 string literal is the same, except prefixed by u. A UTF-32 string literal is the same, except prefixed by U. Collectively, wchar_t, UTF-16, and UTF-32 string literals are called wide string literals.
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 -termination charactercharacter. 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 string literals but 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 also undefined behavior 33 of Annex J of the C Standard [ISO/IEC 9899:2011]..)
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 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 and pointer to (or array of) const
characters shall be treated as a string literal.Do not attempt to modify a string literal. Use a named array of characters to obtain a modifiable string.
Noncompliant Code Example
In this noncompliant code example, the char
pointer p
is str
is initialized to the address of a string literal. Attempting to modify the string literal results in is undefined behavior:
Code Block | ||||
---|---|---|---|---|
| ||||
char *pstr = "string literal"; pstr[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 STR36 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
str
. The string stored in a
can str
can be modified safely modified.
Code Block | ||||
---|---|---|---|---|
| ||||
char astr[] = "string literal"; astr[0] = 'S'; |
Noncompliant Code Example (POSIX)
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> void func(void) { char *fname; fname = 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)
Instead This compliant solution uses a named array instead of passing a string literal, use a named array:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h>
void func(void) {
static char fname[] = "/tmp/edXXXXXX";
mkstemp(fname);
} |
Noncompliant Code Example (Result of strrchr()
)
In this noncompliant example, the non-const
char *
result of the strrchr()
function is used to modify the object pointed to by pathname
. Because the pointer argument to strrchr()
points to a string literal, the effects of the modification are undefined and are likely to cause a signal, such as SIGSEGV
, to be generated for the process if the object is stored in read-only memory.
Code Block | ||||
---|---|---|---|---|
| ||||
#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()
)
A compliant 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 of to callers of get_dirname()
passing constant objects to the function, the argument is declared to be a non-const
pointer. Although converting a string literal to non-const
char*
is permitted by the language, conforming compilers could issue a diagnostic for such code. See also EXP05-C. Do not cast away a const qualification, 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 *
.
Code Block | ||||
---|---|---|---|---|
| ||||
#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, *slashslash_idx); dirname[slash_idx] = '\0'; return dirname; } } return pathname0; } int main(void) { char pathnamedirname[] = __FILE__260]; if /* Calling (get_dirname(__FILE__), may be diagnosed */ dirname, sizeof(dirname))) { puts(get_dirname(pathname)); } 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 | ||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| string-literal-modfication write-to-string-literal | Fully checked | ||||||
Axivion Bauhaus Suite |
| CertC-STR30 | Fully implemented | ||||||
Compass/ROSE |
Can detect simple violations of this rule | |||||||||
Coverity |
| PW | Deprecates conversion from a string literal to "char *" | ||||||
Helix QAC |
| C0556, C0752, C0753, C0754 C++3063, C++3064, C++3605, C++3606, C++3607 | |||||||
| CERT.STR.ARG.CONST_TO_NONCONST | ||||||||
LDRA tool suite |
| 157 S | Partially implemented |
Parasoft C/C++test |
| CERT_C-STR30-a | A string literal shall not be modified | ||||||
PC-lint Plus |
| 489, 1776 | Partially supported | ||||||
Polyspace Bug Finder |
| CERT C: Rule STR30-C | Checks for writing to const qualified object (rule fully covered) | ||||||
PVS-Studio |
| V675 | |||||||
RuleChecker |
| string-literal-modfication | Partially checked |
Splint |
| ||||||||
TrustInSoft Analyzer |
|
mem_access | Exhaustively verified (see one compliant and one non-compliant example). |
Related Vulnerabilities
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 | EXP05-C. Do not cast away a const qualification |
STR36
Prior to 2018-01-12: CERT: Unspecified Relationship | |
CERT C Secure Coding Standard | STR11-C. Do not specify the bound of a character array initialized with a string literal |
Prior to 2018-01-12: CERT: Unspecified Relationship | ||
ISO/IEC TS 17961:2013 | Modifying string literals [strmod] | Prior to 2018-01-12: CERT: Unspecified Relationship |
Bibliography
[ISO/IEC 9899: |
2024] |
6.4.5, "String Literals" | |
[Plum 1991] | Topic 1.26, "Strings—String Literals" |
[Summit 1995] | comp.lang.c FAQ |
List, Question 1.32 |
...
...