The pointer-to-member operators .*
and ->*
are used to obtain an object or a function as though it were a member of an underlying object. For instance, the following are functionally equivalent ways to call the member function f()
on the object o
:.
Code Block |
---|
struct S { void f() {} }; void func() { S o; void (S::*pm)() = &S::f; o.f(); (o.*pm)(); } |
...
The C++ Standard, [expr.mptr.oper], paragraph 4 , states [ISO/IEC 14882-2014], states the following:
Abbreviating pm-expression.*cast-expression as
E1.*E2
,E1
is called the object expression. If the dynamic type ofE1
does not contain the member to whichE2
refers, the behavior is undefined.
(A pointer-to-member expression of the form E1->*E2
is converted to its equivalent form, (*(E1)).*E2
, so use of pointer-to-member expressions of either form behave equivalently in terms of undefined behavior.
Further, the C++ Standard, [expr.mptr.oper], paragraph 6, in part, states the following:
If the second operand is the null pointer to member value, the behavior is undefined.
...
Do not use a pointer-to-member expression where the dynamic type of the first operand does not contain the member to which the second operand refers, including the use of a null pointer-to-member value as the second operand.
Noncompliant Code Example
In this noncompliant code example, a pointer-to-member object is obtained from D::g
, but upcast but is then upcast to be a B::*
. When called on an object whose dynamic type is D
, the pointer-to-member call is well - defined. However, in this noncompliant code example, the dynamic type of the underlying object is object is B
, resulting which results in undefined behavior:.
Code Block | ||||
---|---|---|---|---|
| ||||
struct B {
virtual ~B() = default;
};
struct D : B {
virtual ~D() = default;
virtual void g() { /* ... */ }
};
void f() {
B *b = new B;
// ...
void (B::*gptr)() = static_cast<void(B::*)()>(&D::g);
(b->*gptr)();
delete b;
}
|
Compliant Solution
In this compliant solution, the upcast is removed, rendering the initial code ill-formed. This emphasizes and emphasizing the underlying problem : that B::g()
does not exist. This compliant solution assumed assumes that the programmer intent 's intention was to use the correct dynamic type for the underlying object:.
Code Block | ||||
---|---|---|---|---|
| ||||
struct B { virtual ~B() = default; }; struct D : B { virtual ~D() = default; virtual void g() { /* ... */ } }; void f() { B *b = new D; // Corrected the dynamic object type. // ... void (D::*gptr)() = &D::g; // RemovedMoved static_cast to the next line. (static_cast<D *>(b)->*gptr)(); delete b; } |
Noncompliant Code Example
In this noncompliant code example, a null pointer-to-member value is passed as the second operand to a pointer-to-member expression, resulting in undefined behavior.
Code Block | ||||
---|---|---|---|---|
| ||||
struct B {
virtual ~B() = default;
};
struct D : B {
virtual ~D() = default;
virtual void g() { /* ... */ }
};
static void (D::*gptr)(); // Not explicitly initialized, defaults to nullptr.
void call_memptr(D *ptr) {
(ptr->*gptr)();
}
void f() {
D *d = new D;
call_memptr(d);
delete d;
} |
Compliant Solution
In this compliant solution, gptr
is properly initialized to a valid pointer-to-member value instead of to the default value of nullptr
.
Code Block | ||||
---|---|---|---|---|
| ||||
struct B { virtual ~B() = default; }; struct D : B { virtual ~D() = default; virtual void g() { /* ... */ } }; static void (D::*gptr)() = &D::g; // Explicitly initialized. void call_memptr(D *ptr) { (ptr->*gptr)(); } void f() { D *d = new D; call_memptr(d); delete d; } |
Risk Assessment
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|
OOP55-CPP | High | Probable | High | P6 | L2 |
Automated Detection
Tool | Version | Checker | Description |
---|
Astrée |
| overflow_upon_dereference invalid_function_pointer | |||||||
Axivion Bauhaus Suite |
| CertC++-OOP55 | |||||||
CodeSonar |
| LANG.MEM.UVAR | Uninitialized Variable | ||||||
Helix QAC |
| DF2810, DF2811, DF2812, DF2813, DF2814 | |||||||
Klocwork |
| CERT.OOP.PTR_MEMBER.NO_MEMBER | |||||||
Parasoft C/C++test |
| CERT_CPP-OOP55-a | A cast shall not convert a pointer to a function to any other pointer type, including a pointer to function type | ||||||
Parasoft Insure++ | Runtime detection | ||||||||
Polyspace Bug Finder |
| CERT C++: OOP55-CPP | Checks for pointers to member accessing non-existent class members (rule fully covered). |
Related Vulnerabilities
Search for other vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
|
This rule is a subset of EXP34-C. Do not dereference null pointers.
Bibliography
[ISO/IEC 14882-2014] | Subclause 5.5, "Pointer-to-Member Operators" |
...
OOP37-CPP. Write constructor member initializers in the canonical order 013. Object Oriented Programming (OOP) 014. Concurrency (CON)