[Exceptional C++ Style] [Exception C++ Style] Item 15 - Uses and Abuses of Access Rights
James Gunn
gunnware at yahoo.com
Tue Dec 14 06:19:59 EST 2004
This item discusses one JG question and two Guru
questions.
JG Question
1. What code can access the following parts of a
class?
a) public
b) protected
c) private
Guru Questions
2. Consider the following header file:
// File x.h
//
class X
{
public:
X() : private_(1) {/*...*/}
template<class T>
void f(const T& t) {/*...*/}
int Value() { return private_; }
//...
private:
int private_;
};
Demonstrate:
a) a non-standards-conforming and non-portable hack;
and
b) a fully standards-conforming and portable
technique
for any code to get direct access to this class's
private_ member.
3. Is this a hole in C++'s access control mechanism,
and therefore a hole in C++'s encapsulation?
Solution
1.
Sutter states the answers for what code can access
public, protected and private parts of a class with
private access being the interesting
one. He goes on to state that for private members the
usual answer is only member functions and friends can
access them. Sutter then
discusses a special case where access to a class's
private members can be legally subverted.
2.
Sutter starts this answer by discussing three
non-standards-conforming and non-portable hacks:
The Forger duplicates a class definition to make it
say what he wants it to say. The Forger's hack is
illegal as it breaks the One Definition
Rule (ODR). Sutter states that this will work on most
compilers because the underlying data layout will
usually be the same.
The Pickpocket attempts to change the meaning of the
class definition by redefining private as public:
#define private public
However, the above is illegal as it attempts to
redefine a keyword. It also breaks the ODR and
therefore the same thing applies as was stated
for The Forger.
The Cheat attempts to substitute one item when your
expecting another. For example:
class BaitAndSwitch
{
public:
int notSoPrivate;
};
void f(X& x)
{
(reinterpret_cast<BaitAndSwitch&>(x)).notSoPrivate
= 2;
}
Sutter states that the example, above, is illegal for
two reasons:
The object layouts of X and BaitAndSwitch are not
guaranteed to be the same, although they probably
always will be.The results of the
reinterpret_cast are undefined, although most
compilers will let you try to use the resulting
reference in the way The Cheat intended.
At this point Sutter gives a great desciption of
criminals and hackers before moving on to show a fully
standards-conforming and portable
technique to subvert the access to private members.
Sutter states that The Langugage Lawyer is a
dishonest, toothy-smiled criminal :-)
The following example is given for the legal weasel:
namespace
{
struct Y{};
}
template<>
void X::f(const Y&)
{
private_ = 2; // evil laughter here
}
void Test()
{
X x;
std::cout << x.Value() << std::endl; // prints 1
x.f(Y());
std::cout << x.Value() << std::endl; // prints 2
}
The above is completely legal because, as Sutter
informs us, it exploits the fact that X has a member
template and is fully
standards-conforming and is guaranteed by the standard
to work as expected. There are two reasons for this:
1. It's legal to specialise a member template on any
type.
The only room for error would be if you tried to
specialise it on the same type twice in different
ways, which would violate the ODR, but
we get round that because:
2. The code uses a type that's guaranteed to be
unique, because it's in the hacker's own unnamed
namespace. Therefore it's guaranteed to be
legal and won't stomp on anyone else's specialisation.
Don't Subvert
Sutter then answers the final question:
3. Is this a hole in C++'s access control mechanism,
and therefore a hole in C++'s encapsulation?
Sutter starts his answer by stating that a portable
way to bypass the class's access control mechanism,
and implicitly "break encapsulation"
is provided by member templates. Following on, he
discusses that the issue is about protecting against
accidental misuse as opposed to
protecting against deliberate abuse. The examples in
this item clearly show that if someone wants to they
can subvert the system.
Finally, the real answer to the issue is to not
subvert the access control system. I agree with Sutter
at this point that subverting the
access control system should be a one-warning offence
if it's used in production code.
Guideline:
Never subvert the language. For example, never attempt
to break encapsulation by copying a class definition
and adding a friend declaration
or by providing a local instantiation of a template
member function.
__________________________________
Do you Yahoo!?
Send a seasonal email greeting and help others. Do good.
http://celebrity.mail.yahoo.com
More information about the Effective-cpp
mailing list