CertC++-CTR56¶
Do not use pointer arithmetic on polymorphic objects
Required inputs: IR
The definition of pointer arithmetic from the C++ Standard, [expr.add], paragraph 7 [ ISO/IEC 14882-2014], states the following:
For addition or subtraction, if the expressions
PorQhave type "pointer to cvT", whereTis different from the cv-unqualified array element type, the behavior is undefined. [Note: In particular, a pointer to a base class cannot be used for pointer arithmetic when the array contains objects of a derived class type. -end note]
Pointer arithmetic does not account for polymorphic object sizes, and attempting to perform pointer arithmetic on a polymorphic object value results in undefined behavior.
The C++ Standard, [expr.sub], paragraph 1 [ ISO/IEC 14882-2014], defines array subscripting as being identical to pointer arithmetic. Specifically, it states the following:
The expression
E1[E2]is identical (by definition) to*((E1)+(E2)).
Do not use pointer arithmetic, including array subscripting, on polymorphic objects.
The following code examples assume the following static variables and class definitions.
int globI;
double globD;
struct S {
int i;
S() : i(globI++) {}
};
struct T : S {
double d;
T() : S(), d(globD++) {}
};
Noncompliant Code Example (Pointer Arithmetic)
In this noncompliant code example,
f() accepts an array of
S objects as its first parameter. However,
main() passes an array of
T objects as the first argument to
f(), which results in
undefined
behavior due to the pointer arithmetic used within the
for loop.
#include <iostream>
// ... definitions for S, T, globI, globD ...
void f(const S *someSes, std::size_t count) {
for (const S *end = someSes + count; someSes != end; ++someSes) {
std::cout << someSes->i << std::endl;
}
}
int main() {
T test[5];
f(test, 5);
}
Noncompliant Code Example (Array Subscripting)
In this noncompliant code example, the
for loop uses array subscripting. Since array
subscripts are computed using pointer arithmetic, this code also results in
undefined behavior.
#include <iostream>
// ... definitions for S, T, globI, globD ...
void f(const S *someSes, std::size_t count) {
for (std::size_t i = 0; i < count; ++i) {
std::cout << someSes[i].i << std::endl;
}
}
int main() {
T test[5];
f(test, 5);
}
Compliant Solution (Array)
Instead of having an array of objects, an array of pointers solves the problem of the objects being of different sizes, as in this compliant solution.
#include <iostream>
// ... definitions for S, T, globI, globD ...
void f(const S * const *someSes, std::size_t count) {
for (const S * const *end = someSes + count; someSes != end; ++someSes) {
std::cout << (*someSes)->i << std::endl;
}
}
int main() {
S *test[] = {new T, new T, new T, new T, new T};
f(test, 5);
for (auto v : test) {
delete v;
}
}
The elements in the arrays are no longer polymorphic objects (instead, they are pointers to polymorphic objects), and so there is no undefined behavior with the pointer arithmetic.
Compliant Solution (
std::vector)
Another approach is to use a standard template library (STL) container
instead of an array and have
f() accept iterators as parameters, as in this compliant solution.
However, because STL containers require homogeneous elements, pointers are
still required within the container.
#include <iostream>
#include <vector>
// ... definitions for S, T, globI, globD ...
template <typename Iter>
void f(Iter i, Iter e) {
for (; i != e; ++i) {
std::cout << (*i)->i << std::endl;
}
}
int main() {
std::vector<S *> test{new T, new T, new T, new T, new T};
f(test.cbegin(), test.cend());
for (auto v : test) {
delete v;
}
}
Risk Assessment
Using arrays polymorphically can result in memory corruption, which could lead to an attacker being able to execute arbitrary code.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| CTR56-CPP | High | Likely | High | P9 | L2 |
Related Guidelines
| SEI CERT C Coding Standard | ARR39-C. Do not add or subtract a scaled integer to a pointer |
Bibliography
| [ ISO/IEC 14882-2014] | Subclause 5.7, "Additive Operators" Subclause 5.2.1, "Subscripting" |
| [ Lockheed Martin 2005] | AV Rule 96, "Arrays shall not be treated polymorphically" |
| [ Meyers 1996] | Item 3, "Never Treat Arrays Polymorphically" |
| [ Stroustrup 2006] | "What's Wrong with Arrays?" |
| [ Sutter 2004] | Item 100, "Don't Treat Arrays Polymorphically" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
non_final_pointer_arithmetic |
Pointer arithmetic on non-final classes not allowed |
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
consider_non_base_final¶
consider_non_base_final : bool = False