CertC++-EXP55

Do not access a cv-qualified object through a cv-unqualified type

Required inputs: IR

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

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 the following:

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 the resulting object. 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 g() is passed a const int &, which is then cast to an int & and modified. Because the referenced value was previously declared as const, the assignment operation results in undefined behavior.

void g(const int &ci) {
  int &ir = const_cast<int &>(ci);
  ir = 42;
}

void f() {
  const int i = 4;
  g(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.

void g(int &i) {
  i = 42;
}

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

In this noncompliant code example, a  const-qualified method is called that attempts to cache results by casting away the const-qualifier of  this. Because s was declared  const, the mutation of  cachedValue results in undefined behavior.

#include <iostream>
 
class S {
  int cachedValue;

  int compute_value() const;  // expensive
public:
  S() : cachedValue(0) {}

  // ...
  int get_value() const {
    if (!cachedValue) {
      const_cast<S *>(this)->cachedValue = compute_value();
    }
    return cachedValue;
  }
};

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

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

#include <iostream>

class S {
  mutable int cachedValue;

  int compute_value() const;  // expensive
public:
  S() : cachedValue(0) {}

  // ...
  int get_value() const {
    if (!cachedValue) {
      cachedValue = compute_value();
    }
    return cachedValue;
  }
};

void f() {
  const S s;
  std::cout << s.get_value() << 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 behavior.

#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, so  g() is modified to accept a  volatile S &.

#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

EXP55-CPP-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. However, it is always preferable to modify the API to be const-correct when possible. For example, the following code casts away the const qualification of INVFNAME in the call to the audit_log() function.

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

void f() {
  const char INVFNAME[]  = "Invalid file name.";
  audit_log(const_cast<char *>(INVFNAME));
}
Risk Assessment

If the object is declared as being constant, it may reside in write-protected memory at runtime. Attempting to modify such an object may lead to 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.

Rule Severity Likelihood Remediation Cost Priority Level
EXP55-CPP Medium Probable Medium P8 L2
Related Guidelines
SEI CERT C Coding Standard EXP32-C. Do not access a volatile object through a nonvolatile reference
EXP40-C. Do not modify constant objects 
Bibliography
[ ISO/IEC 14882-2014] Subclause 7.1.6.1, "The cv-qualifiers"
[ Sutter 2004] Item 94, "Avoid Casting Away const"
Excerpt from SEI CERT C++ Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-cpp-coding-standard/rules/expressions-exp/exp55-cpp], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

cast_removes_atomic

Cast removes _Atomic qualification

None

False

cast_removes_const

Cast removes const qualification

None

False

cast_removes_volatile

Cast removes volatile qualification

None

False

implicit_cast_removes_atomic

Implicit cast removes _Atomic qualification

None

False

implicit_cast_removes_const

Implicit cast removes const qualification

None

False

implicit_cast_removes_volatile

Implicit cast removes volatile qualification

None

False

Options

check_atomic

check_atomic : bool = False

Whether casts affecting _Atomic-qualifiers should be reported
 

check_const

check_const : bool = True

Whether casts affecting const-qualifiers should be reported.
 

check_volatile

check_volatile : bool = True

Whether casts affecting volatile-qualifiers should be reported.
 

only_top_level

only_top_level : bool = False

Whether the check only applies to the top-level pointee or recursively to deeper pointer levels as well.