Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: fixed minor typo in C quote

According to the C Standard, subclause 6.4.5 paragraph 3 [ISO/IEC 9899:2011],

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 wide string literal is the same, except prefixed by the letter L, u, or U.

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. 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 string literals. 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.

String literals are usually referred to via a pointer to, or array of characters. Ideally they should be assigned only to pointers to (or arrays of) const char.

When called with a pointer to (or array of) const characters, the return value from the following functions shall be treated as a pointer to const characters:

  • strpbrk(), strchr(), strrchr(), strstr()
  • wcspbrk(), wcschr(), wcsrchr(), wcsstr()
  • memchr(), wmemchr()

Do not attempt to modify a string literal. Instead, use a named array of characters to create a modifiable copy of a string literal.

This rule is an 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 results in undefined behavior:

Code Block
bgColor#FFcccc
langc
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 STR36-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 safely modified.

Code Block
bgColor#ccccff
langc
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:

Code Block
bgColor#FFcccc
langc
#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)

Instead of passing a string literal, use a named array:

Code Block
bgColor#ccccff
langc
#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 pointer points to a string literal, the effects of the modification are undefined.

Code Block
bgColor#FFcccc
langc
#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() {
  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 of 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 *.

Code Block
bgColor#ccccff
langc
#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() {
  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

Compass/ROSE

  

Can detect simple violations of this rule

LDRA tool suite

Include Page
LDRA_V
LDRA_V

157 S

Partially implemented
PRQA QA-C
Include Page
PRQA_V
PRQA_V
0556Partially implemented

Splint

Include Page
Splint_V
Splint_V
 

 

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"
Annex J, subclause J.2, "Undefined Behavior"
[Plum 1991]Topic 1.26, "Strings—String Literals"
[Summit 1995]comp.lang.c FAQ list, Question 1.32