Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The C++ Standard, [dcl.type.cv], paragraph 4, states [ISO/IEC 14882-2014]:

Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.

Similarly, paragraph 6 states:

What constitutes an access to an object that has volatile-qualified type is implementation-defined. If an attempt is made to refer to an object defined with a volatile-qualified type through the use of a glvalue with a non-volatile-qualified type, the program behavior is undefined.

Do not cast away a const qualification to attempt to modify an the resulting object declared as const. The specification of const in variables and parameters implies to a maintainer or caller that, despite knowing some memory location, the code will not modify its content. Although C++ allows you to remove the specifier using typecasts, doing so violates the implication of the specifier.

...

. The const qualifier implies that the API designer does not intend for that object to be modified, despite the possibility it may be modifiable. Do not cast away a volatile qualification; the volatile qualifier implies that the API designer intends the object to be accessed in ways unknown to the compiler, and any access of the volatile object results in undefined behavior.

Noncompliant Code Example

In this noncompliant code example, the function fg() is passed a const char pointer. It then typecasts the const specification away and proceeds to modify the contents int &, which is then cast to an int & and modified. Because the value referenced to was previously declared as const, the assignment operation results in undefined behavior.

Code Block
bgColor#FFcccc
langcpp
void fg(char const *str, int slen&ci) {
  int char&ir *p = const_cast<char*cast<int &>(strci);
  ir int= i42;
}

 
void f() {
  forconst int (i = 04;
  g(i < slen && str[i]; i++);
}

Compliant Solution

In this compliant solution, the function g() is passed an int &, and the caller is required to pass an int that can be modified.

Code Block
bgColor#ccccff
langcpp
void g(int &i) {
  i =   if (str[i] != ' ') *p++ = str[i];
   }
}

Compliant Solution

42;
}

void f() {
  int i = 4;
  g(i);
}

Noncompliant Code Example

In this noncompliant code example, a const-qualified method is called which attempts to cache results by casting away the const-qualifier of this. Since s was declared const, the mutation of CachedCalc results in undefined behaviorIn this compliant solution, the function f is passed a non-const char pointer. The calling function must ensure that the null-terminated byte string passed to the function is not const by making a copy of the string or by other means.

Code Block
bgColor#ccccff#FFcccc
langcpp
void f(char *str, int slen#include <iostream>
 
class S {
  int CachedCalc;
  
  int expensiveCompute() const;  
public:
  S() : CachedCalc(0) {}
  
  // ...  
  int Calculate() const {
    if (!CachedCalc) {
     char const_cast<S *p>(this)->CachedCalc = str;expensiveCompute();  
    }        
    return CachedCalc;
  }
};

void f() {
  const intS is;
  std::cout for<< (i = 0; i < slen && str[i]; i++) {
      if (str[i] != ' ') *p++ = str[i];
   }
}

Non-Compliant Code Example

s.Calculate() << std::endl;
}

Compliant Solution

This compliant solution uses the mutable keyword when declaring CachedCalc, which allows it to be mutated within a const context without triggering undefined behavior:

Code Block
bgColor#ccccff
langcpp
#include <iostream>
 
class S {
  mutable int CachedCalc;
  
  int expensiveCompute() const;
public:
  S() : CachedCalc(0) {}
  
  // ...  
  int Calculate() const {
    if (!CachedCalc) {
      CachedCalc = expensiveCompute();  
    }        
    return CachedCalc;
  }
};

void f() {
  const S s;
  std::cout << s.Calculate() << std::endl;
}

Noncompliant Code Example

In this noncompliant code example, the volatile value s has the volatile qualifier cast away, and an attempt is made to read the value within g(), resulting in undefined behaviorIn this example, a const int array vals is declared, and then its content is modified by calling memset with the function, leading to values of 0 in the vals array.

Code Block
bgColor#FFcccc
langcpp
int const vals[] = {3, 4, 5};
memset((int*) vals, 0, sizeof(vals));

Compliant Solution

If the intention is to allow the array values to be modified, do not declare the array as const.

Code Block
bgColorccccff
langcpp
int vals[] = {3, 4, 5};
memset(vals, 0, sizeof(vals));

Otherwise, do not attempt to modify the contents of the array.

Exceptions

#include <iostream>

struct S {
  int i;
  
  S(int i) : i(i) {}
};

void g(S &s) {
  std::cout << s.i << std::endl;
}

void f() {
  volatile S s(12);
  g(const_cast<S &>(s));
}

Compliant Solution

This compliant solution assumes that the volatility of s is required, and so g() is modified to accept a volatile S &:

Code Block
bgColor#ccccff
langcpp
#include <iostream>

struct S {
  int i;
  
  S(int i) : i(i) {}
};

void g(volatile S &s) {
  std::cout << s.i << std::endl;
}

void f() {
  volatile S s(12);
  g(s);
}

Exceptions

EXP35EXP05-EX1: An exception to this rule is allowed when it is necessary to cast away const when invoking a legacy API that does not accept a const argument, provided the function does not attempt to modify the referenced variable. For . It is always preferable to modify the API to be const-correct when possible, however. For example, the following code casts away the const qualification of INVFNAME in the call to the audit_log() function.

Code Block
//* Legacy function defined elsewhere - cannot be modified */; does not attempt to
// modify the contents of the passed parameter.
void audit_log(char *errstr) {;

void  fprintff(stderr, "Error: %s.\n", errstr);
}

/* ... */
) {
  const char INVFNAME[]  = "Invalid file name.";
  audit_log(const_cast<char *>(INVFNAME)); /*/ EXP05EXP35-EX1 */
/* ... */
}

Risk Assessment

If the object really is declared as being constant, the compiler may have put it in ROM or it may reside in write-protected memory at runtime. Trying Attempting to modify such an object may lead to a program crash. This could allow an attacker to mount abnormal program termination, or a denial-of-service attack. If an object is declared as being volatile, the compiler can make no assumptions regarding access of that object. Casting away the volatility of an object can result in reads or writes to the object being reordered, or elided entirely, resulting in abnormal program execution.

RuleRecommendation

Severity

Likelihood

Remediation Cost

Priority

Level

EXP35-CPP

mediumMedium

probableProbable

mediumMedium

P8

L2

Automated Detection

The LDRA tool suite V 7.6.0 can detect violations of this recommendation.

GCC Compiler can detect violations of this rule when the -Wcast-qual flag is used.

Compass/ROSE can detect violations of this recommendation.

Can detect violations of this recommendation when the -Wcast-qual flag is used.

Tool

Version

Checker

Description

Section

LDRA tool suite

7.6.0

  
Section

GCC

  
Section
Section

Compass/ROSE

 

 

 

ECLAIR

Include Page
ECLAIR_VECLAIR_V

CP1.EXP35

Fully implemented

PRQA QA-C++
Include Page
PRQA QA-C++_vPRQA QA-C++_v3060,3083 

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Other Languages

...

Related Guidelines

Bibliography

...

2014]7.1.6.1, "The cv-qualifiers"
[Sutter 04]Item 94

...

, "Avoid casting away const"

 .

      03003. Expressions (EXP)      EXP36-CPP. Do not cast pointers into more strictly aligned pointer types