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
mutablecan be modified, any attempt to modify aconstobject 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
keyword when declaring
mutablecachedValue, 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"
|
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¶
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
check_atomic¶
check_atomic : bool = False
check_const¶
check_const : bool = True
check_volatile¶
check_volatile : bool = True
only_top_level¶
only_top_level : bool = False