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.
As an illustration, C99 provides a footnote:
...
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
The remove_spaces()
function in In this noncompliant code example accepts a pointer to a string str
and a string length slen
and removes the space character from the string by shifting the remaining characters toward the front of the string. The function remove_spaces, the function g
() is passed a const char
pointer as an argument. The const
qualification is cast away and then the contents of the string are modifiedint &, 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.
Code Block | ||||
---|---|---|---|---|
| ||||
void remove_spacesg(const char *str, size_t slen int &ci) { int &ir = const_cast<int &>(ci); ir = 42; } void f() { const charint *pi = 4; g(char *i)str; size_t i; for (i = 0; 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 | ||||
---|---|---|---|---|
| ||||
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.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> class S { int cachedValue; int compute_value() const; // expensive public: S() : cachedValue(0) {} // ... int get_value() const { if (str[i] != ' ') *p++ = str[i]; } *p = '\0'; } |
Compliant Solution
!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 mutable
cachedValue
, which allows cachedValue
to be mutated within a const
context without triggering undefined behaviorIn this compliant solution, the function remove_spaces()
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 | ||||
---|---|---|---|---|
| ||||
#include <iostream> void remove_spaces(char *str, size_t slen) { char *p = str; size_t i; for (i = 0; i < slen && str[i]; i++) class S { mutable int cachedValue; int compute_value() const; // expensive public: S() : cachedValue(0) {} // ... int get_value() const { if (str[i] != ' ') *p++ = str[i]; } *p = '\0'; } !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 contents of the const
int
array vals
are cleared by the call to memset()
volatile value s
has the volatile
qualifier cast away, and an attempt is made to read the value within g()
, resulting in undefined behavior.
Code Block | ||||
---|---|---|---|---|
| ||||
const int vals[3] = {3, 4, 5};
memset(vals, 0, sizeof(vals));
|
Because the memset()
function takes a (non-const
) pointer to void
, the compiler must implicitly cast away const
.
Implementation Details
The compiler GCC issues a warning when an implicit cast is performed.
Compliant Solution
#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 &
If the intention is to allow the array values to be modified, do not declare the array as const
.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> struct S { int vals[3] = {3, 4, 5}; memset(vals, 0, sizeof(vals)); |
Otherwise, do not attempt to modify the contents of the array.
Exceptions
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-CPPEXP05-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 . 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.
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((charconst_cast<char *>(INVFNAME)INVFNAME); /* EXP05-EX1 */ /* ... */ } |
Risk Assessment
If the object is declared as being constant, the compiler it may allocate storage reside in ROM or write-protected memory at runtime. Attempting to modify such an object may lead to a abnormal program crash 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
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.
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.
Other Languages
...
Related Guidelines
...
...
Bibliography
[ISO/IEC 14882-2014] | Subclause 7.1.6.1, "The cv-qualifiers" |
[Sutter 2004] | Item 94, "Avoid Casting Away const " |
...
References
Wiki Markup |
---|
\[[ISO/IEC 9899:1999|AA. C++ References#ISO/IEC 9899-1999]\] Section 6.7.3, "Type qualifiers"
\[[ISO/IEC PDTR 24772|AA. C++ References#ISO/IEC PDTR 24772]\] "HFC Pointer casting and pointer type changes" and "IHN Type system"
\[[MISRA 04|AA. C++ References#MISRA 04]\] Rule 11.5 |
EXP04-CPP. Do not perform byte-by-byte comparisons between structures 03. Expressions (EXP) EXP06-CPP. Operands to the sizeof operator should not contain side effects