Can one access private member functions through ca

2020-07-13 09:17发布

问题:

From the discussion of this question How is access for private variables implemented in C++ under the hood? I posed a variation: instead of accessing a private data member, can one call private member functions through casting and relying on layout-compatibility?

Some code (inspired by Herb Sutter's column Uses and Abuses of Access Rights )

#include <iostream>

class X 
{ 
public:
  X() : private_(1) { /*...*/ }

private: 
  int Value() { return private_; }
  int private_; 
};

// Nasty attempt to simulate the object layout
// (cross your fingers and toes).
//
class BaitAndSwitch
    // hopefully has the same data layout as X
{   // so we can pass him off as one
public:
  int Value() { return private_; }
private:
  int private_;
};

int f( X& x )
{
  // evil laughter here
  return (reinterpret_cast<BaitAndSwitch&>(x)).Value();
}

int main()
{
    X x;
    std::cout << f(x) << "\n"; // prints 0, not 1
    return 0;
}; 

Note: this works (at least on Ideone)! Is there any way the new C++11 Standard gives a guaranteed or at least an implementation-defined way to circumvent the access control by relying on layout-compatibility and reinterpret_cast / static_cast?

EDIT1: output on Ideone

EDIT2: In Sutter's column he lists two reasons why the above code is not guaranteed to work (although it works in practice)

a) The object layouts of X and BaitAndSwitch are not guaranteed to be the same, although in practice they probably always will be.

b) The results of the reinterpret_cast are undefined, although most compilers will let you try to use the resulting reference in the way the hacker intended.

Does the new C++11 Standard now provide these layout / reinterpret_cast guarantees?

回答1:

Yes, you could create a type that uses the same layout as the type you're trying to pilfer from, then reinterpret_cast from that type to your layout compatible type. But this is only protected by the standard if both the source and destination types are standard layout types (and of course, it only actually works if their layouts are the same). So if the source has virtual functions, you're screwed.

This seems to satisfy both of Sutter's issues here. The rules of standard layout ensure that two types that are both standard layout that define the same members in the same order are layout-compatible (section 9.2, paragraph 17):

Two standard-layout struct (Clause 9) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible types (3.9).

And the rules for reinterpret_cast specify the meaning of the conversion between two standard layout types (section 5.2.10, paragraph 7):

An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void.