This is not my original idea. I probably stumbled by accident upon work of Johannes Schaub - litb at his personal blog: litb's Blog. Despite it's limited practical value I liked it for the insight and doing the "impossible". In this short article I would try to explain as simple as possible the core idea.
Critical observation is that access specifiers (e. g. private
) are not taken into account during explicit template instantiation. C++ 1998, 2003 and 2011 standards say so (always section 14.7.2 yet different paragraphs).
ISO/EIC 14882:2011 14.7.2 paragraph 12The usual access checking rules do not apply to names used to specify explicit instantiations. [ Note: In particular, the template arguments and names used in the function declarator (including parameter types, return types and exception specifications) may be private types or objects which would normally not be accessible and the template may be a member template or member function which would not normally be accessible. - end note ]
Let's show how to exploit this feature in practice on trivial structure with private data member.
#include <iostream>
struct safe {
private:
const int data = 42;
};
int main() {
safe the_one;
/* our definitely not noble quest:
std::cout << the_one.data;
*/
return 0;
}
Corner-stone will be misuse of above mentioned "feature" as it is the only place where private member name can be used legally (AFAIK ATM). Yet clever way to use it without need to mention the member name elsewhere needs to be used.
#include <iostream>
struct safe {
private:
const int data = 42;
};
template<const int safe::* MEMBER_INT_PTR> Surprise;
template struct Surprise<&safe::data>;
int main() {
safe the_one;
/* compile time error:
the_one.data;
*/
return 0;
}
Just keep in mind that class template definition on it's own does not affect runtime unless compile-time-called. It just describes way how to compile-time-instantiate class definitions. To actually define some class compile-time-instantiation of class template is needed.
Template instantiation (please don't confuse with template specialization) doesn't do much unless you are creative. The trick is to use friend
function definition in template class.
Among other (more common) things friend
keyword let you define function inside class declaration that (apart from having access to class private members/methods) belongs to the scope enclosing the class declaration. In other words - the new function is defined outside of the class.
class a {
friend void my_function() { /* see it is a definition */ }
};
That's it - we are basically done. To define some generic template so that it prints member referred to by pointer parameter is trivial and all that's left is to call it.
All put together it might looks like this:
#include <iostream>
struct safe {
private:
const int data = 42;
};
template<const int safe::* MEMBER_INT_PTR>
struct GenerateThiefFunction {
friend void steal_from(const safe& victim_object) {
// dereferencing member pointer on instance - might look exotic but no magic here
std::cout << victim_object.*MEMBER_INT_PTR << std::endl;
}
};
// actually defines the class and therefore defines the steal_from() function
template struct GenerateThiefFunction<&safe::data>;
int main() {
safe the_one;
steal_from(the_one);
return 0;
}
There's just one last catch - such function is not used in name lookup unless either:
Or from the horse's mouth:
ISO/EIC 14882:2011 7.3.1.2 paragraph 3If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2).
Since I am trying to show as simple proof-of-concept as possible declaration is a winner:
#include <iostream>
struct safe {
private:
const int data = 42;
};
template<const int safe::* MEMBER_INT_PTR>
struct GenerateThiefFunction {
friend void steal_from(const safe& victim_object) {
std::cout << victim_object.*MEMBER_INT_PTR << std::endl;
}
};
template struct GenerateThiefFunction<&safe::data>;
void steal_from(const safe& victim_object);
int main() {
safe the_one;
steal_from(the_one);
return 0;
}
If you are to repeat the code for every private member you want to get access to it gets tedious. So if you really need to do sabotage encapsulation this way you might find some inspiration in original article which has some template parameters and ADL on top of the above.
Since this is rather extreme use of obscure language feature I would not be surprised if there's compiler out there that is not compliant to standard regarding disregard for access specifiers. I wouldn't expect this hack to work just because standard demands it.