You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 13 Next »

The pointer-to-member operators .* and ->* are used to obtain an object or 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:

struct S {
  void f() {}
};

void func() {
  S o;
  void (S::*pm)() = &S::f;
  
  o.f();
  (o.*pm)();
}

The call of the form o.f() uses class member access at compile time to look up the address of the function S::f() on the object o. The call of the form (o.*pm)() uses the pointer-to-member operator .* to call the function at the address specified by pm. In both cases, the object o is the implicit this object within the member function S::f().

The C++ Standard, [expr.mptr.oper], paragraph 4, states [ISO/IEC 14882-2014]:

Abbreviating pm-expression.*cast-expression as E1.*E2, E1 is called the object expression. If the dynamic type of E1 does not contain the member to which E2 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 states, in part:

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 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 B, resulting in undefined behavior:

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 the underlying problem: that B::g() does not exist. This compliant solution assumed the programmer intent was to use the correct dynamic type for the underlying object:

struct B {
  virtual ~B() = default;
};

struct D : B {
  virtual ~D() = default;
  virtual void g() { /* ... */ }
};

void f() {
  B *b = new D; // Corrected dynamic object type
 
  // ...
  void (D::*gptr)() = &D::g; // Removed static_cast  
  (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:

struct B {
  virtual ~B() = default;
};

struct D : B {
  virtual ~D() = default;
  virtual void g() { /* ... */ }
};
 
static void (D::*gptr)();
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 the default value of nullptr:

struct B {
  virtual ~B() = default;
};
 
struct D : B {
  virtual ~D() = default;
  virtual void g() { /* ... */ }
};
 
static void (D::*gptr)() = &D::g;
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

    

Related Vulnerabilities

Search for other vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

 

 

Bibliography

[ISO/IEC 14882-2014]

5.5, "Pointer-to-Member Operators"

 

      Rec. 09: Object Oriented Programming (OOP)      Rec. 11: Concurrency (CON)

  • No labels