CertC-STR30¶
Do not attempt to modify string literals
Required inputs: IR
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
str is initialized to the address of a string literal.
Attempting to modify the string literal is
undefined
behavior:
char *str = "string literal"; str[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
str. The string stored in
str can be modified safely.
char str[] = "string literal"; str[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 |
Related Guidelines
| Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| CERT C Secure Coding Standard | EXP05-C. Do not cast away a const qualification | 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:2011] | 6.4.5, "String Literals" |
| [ Plum 1991] | Topic 1.26, "Strings-String Literals" |
| [ Summit 1995] | comp.lang.c FAQ List, Question 1.32 |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
call_with_const |
Result of call to {}() with ‘{}’ input should be used as ‘const {}*’. |
None |
False |
call_with_literal |
Result of call to {}() with string literal should be used as ‘const {}*’. |
None |
False |
nonconst_string_literal |
String literal should only be used as ‘const char*’ |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
funcs¶
funcs
Functions to check.Type: set[bauhaus.analysis.config.QualifiedName]
Default:
{'memchr', 'strchr', 'strpbrk', 'strrchr', 'strstr', 'wcschr', 'wcspbrk', 'wcsrchr', 'wcsstr', 'wmemchr'}