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 aconst
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.
...
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 referenced to was previously declared as const
as const, the assignment operation results in undefined behavior.
...
In this noncompliant code example, a const
-qualified method is called , which that attempts to cache results by casting away the const
-qualifier of this
. Because s
was declared const
, the mutation of CachedCalc
results cachedValue
results in undefined behavior.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> class S { int CachedCalccachedValue; int expensiveComputecompute_value() const; // expensive public: S() : CachedCalccachedValue(0) {} // ... int Calculateget_value() const { if (!CachedCalccachedValue) { const_cast<S *>(this)->CachedCalc>cachedValue = expensiveComputecompute_value(); } return CachedCalccachedValue; } }; void f() { const S s; std::cout << s.Calculateget_value() << std::endl; } |
Compliant Solution
This compliant solution uses the
keyword when declaring mutable
CachedCalc
cachedValue
, which allows it cachedValue
to be mutated within a const
context without triggering undefined behavior:.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> class S { mutable int CachedCalccachedValue; int expensiveComputecompute_value() const; // expensive public: S() : CachedCalccachedValue(0) {} // ... int Calculateget_value() const { if (!CachedCalccachedValue) { CachedCalccachedValue = expensiveComputecompute_value(); } return CachedCalccachedValue; } }; void f() { const S s; std::cout << s.Calculateget_value() << std::endl; } |
Noncompliant Code Example
...
This compliant solution assumes that the volatility of s
is required, and so g()
is modified to accept a volatile S &
:.
Code Block | ||||
---|---|---|---|---|
| ||||
#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); } |
...
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. It However, 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 f() { const char INVFNAME[] = "Invalid file name."; audit_log(const_cast<char *>(INVFNAME)); // EXP35-EX1 } |
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 |
Automated Detection
Tool | Version | Checker | Description |
---|
Astrée |
| pointer-qualifier-cast-const pointer-qualifier-cast-volatile | Partially checked | ||||||
Axivion Bauhaus Suite |
| CertC++-EXP55 | |||||||
Helix QAC |
| C++3066, C++4671 | |||||||
Klocwork |
| MISRA.CAST.CONST | |||||||
LDRA tool suite |
| 203 S, 242 S, 344 S | Fully implemented | ||||||
Parasoft C/C++test |
| CERT_CPP-EXP55-a | A cast shall not remove any 'const' or 'volatile' qualification from the type of a pointer or reference | ||||||
Polyspace Bug Finder |
| CERT C++: EXP55-CPP | Checks for casts that remove cv-qualification of pointer (rule partially covered) | ||||||
RuleChecker |
| pointer-qualifier-cast-const pointer-qualifier-cast-volatile | Partially checked | ||||||
SonarQube C/C++ Plugin |
| S859 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
SEI CERT C Coding Standard | EXP32-C. Do not access a volatile object through a nonvolatile reference |
Bibliography
[ISO/IEC 14882-2014] | Subclause 7.1.6.1, "The cv-qualifiers" |
[Sutter |
2004] | Item 94, "Avoid Casting Away const " |
...
...