Posts Tagged ‘C++ access mechanism’

Recently, I’ve been reading Sutter‘s Exceptional C++ Style and I’ve found a couple of interest items I’d like to discuss. They are about (subtle and evil) tricks to get access to class’ internals.

This is the code Sutter uses:


class X {

public:

     X() : private_(1) {  }

     template <class T>
     void f (const T& t) {  }

     int Value() { return private_; }

private:

     int private_;

};

Sutter creates hacks of this code to get direct access to X’s private_ member. He defines some categories of bad guys that violate the programmer’s intent of keeping private_ under the hood.

The first three hacks are non-standards-conforming and non-portable, but they are quite interesting.

Let’s go.

The forger duplicates a forged class definition to make it say what he wants it to say:


class X {

public:
     // instead of including X.h, manually duplicates (illegal) X's definition and add:
     friend ::Hijack(X&);
};

void Hijack(X& x) { x.private_ = 2; } // never trust to your friends!

This is illegal because it violates the One Definition Rule: if a certain type (say X) is defined more than once, the definitions have to be identical. But be careful: if the underlying object data layout is the same, this hack will work on many compilers. Alas.

The second rascal is the pickpocket. He silently changes the meaning of the class definition:


#define private public

#include "X.h"
void Hijack(X& x) { x.private_ = 2; } // free admission!

This sounds like illegal, doesn’t it? Yes, because it is not permitted to #define a reserved word.

Another plotter is the cheat. His modus operandi is to substitute one item when you’re expecting another:


class MyEvilX {

public:

     int notSoPrivate;

};

void f(X& x) {
     (reinterpret_cast<MyEvilX> (x)).notSoPrivate = 2;
}

It is illegal for two reasons: the object layouts are not guaranteed to be the same (but probably will be… ) and the result of a reinterpret_cast  is undefined (but…).

Finally, Sutter explains a fully standards-conforming and portable technique: the language lawyer:

namespace {
	struct Y {};
}

template <>
void X::f(const Y&) {
	private_ = 2;
}

void Test() {
	X x;
	std::cout << x.Value() << std::endl;
	x.f(Y());
	std::cout << x.Value() << std::endl;
}

This artful thief will never be caught because it work is not illegal! In fact it is legal to specialize a member template of any type (besides, the hacker uses a type that resides in his own unnamed namespace, that won’t tromp on anyone else’s specialization).

Sutter’s short chapter demonstrates that a programmer can find a way to subvert the system.

C++’s leak?

No. Simply, protecting against deliberate abuse is effectively impossible. Just say no. Don’t subvert the system. You’re a big boy now!

In conclusion, I’m going to show you another technique to bypass the access mechanism (still taken from Sutter’s book). It’s about granting access:

class FreeAdmission;

typedef int (FreeAdmission::*PMember)(int);

class FreeAdmission {
public:
     PMember Grant() { return &FreeAdmission::ToHide; }

private:
     int ToHide(int i);
};

int main() {

     FreeAdmission freeAdmission;
     PMember p = freeAdmission.Grant();
     int toHideResult = (FreeAdmission.*p)(21);
}

This code emphasizes the definition of a private member of a class: its name can be used only by member and friends of the class in which it is declared. In the example above the private member function of FreeAdmission is executed in the main function, just because a pointer to ToHide was passed out!

I hope this post was useful to look at private members from another point of view!

Advertisements