May pointer to members circumvent the access level of a member?
Asked Answered
L

2

13

Our infamous litb has an interesting article on how to circumvent the access check.

It is fully demonstrated by this simple code:

#include <iostream>

template<typename Tag, typename Tag::type M>
struct Rob { 
  friend typename Tag::type get(Tag) {
    return M;
  }
};

// use
struct A {
  A(int a):a(a) { }
private:
  int a;
};

// tag used to access A::a
struct A_f { 
  typedef int A::*type;
  friend type get(A_f);
};

template struct Rob<A_f, &A::a>;

int main() {
  A a(42);
  std::cout << "proof: " << a.*get(A_f()) << std::endl;
}

Which compiles and runs (output 42) with gcc 4.3.4, gcc 4.5.1, gcc 4.7.0 (see user1131467's comment) and compiles with Clang 3.0 and Comeau C/C++ 4.3.10.1 in C++03 strict mode and MSVC 2005.

I was asked by Luchian on this answer in which I used it to justify that it was actually legal. I agree with Luchian that it is weird, however both Clang and Comeau are close contenders for the most "Standard" compilers available (much more so than MSVC by default)...

And I could not find anything in the drafts of the Standards I have available (n3337 being the last version I got my hands on).

So... can anyone actually justifies that it is legal or not ?

Lithotomy answered 28/3, 2012 at 12:18 Comment(5)
FYI This outputs proof:42 with g++-4.7 (Debian 4.7.0-1) 4.7.0 in both -std=c++11 and -std=gnu++11Mistreat
Sorry, this is my bad. This does compile, what didn't compile was https://mcmap.net/q/598315/-calling-private-method-in-c - and I was trying to access a private function, not data member.Np
Btw I'm still looking for an answer to that, if the answer worked, it would have been exactly what I've been looking for, but it doesn't.Np
I'm glad that it works across implementations.Siana
@JohannesSchaub-litb: Honestly, it still thinks like an unintended effect of §14.7.2/12. Being able to circumvent privacy "legally" is probably not desirable.Lithotomy
C
13

Yes, it's legal. The relevant text is at §14.7.2/12, talking about explicit template instantiation:

12 The 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 ]

Emhpasis mine.

Cacilie answered 28/3, 2012 at 12:35 Comment(7)
Ah! This is probably what I like the most about the Standard, to have a comprehensive view of something, you just need to trudge through the whole thing and piece the pieces together.Lithotomy
@MatthieuM.: But that's also why I hate the standard! :) EDIT: Whoosh!Cacilie
This was meant to be ironic ;) Though now that it is a on github, perhaps could we propose an edit to cross-link this paragraph from the James cited!Lithotomy
@MatthieuM.: Ah yes, I forgot about that change. ...I'll let you do it. I'd probably just embarrass myself.Cacilie
@mat oh nice. do you happen to know the github url?Siana
@ildjarn oh i see now. I thought you were talking about my little hack.Siana
@GManNickG: I have never submitted a Defect Report so I contacted the maintainer of the core language to ask for his guidance.Lithotomy
C
5

The code is clearly illegal (and requires a compile time diagnostic). In the line:

template struct Rob<A_f, &A::a>;

the expression A::a accesses a private member of A.

The standard is very clear about this: “Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions.“ (§11/4, emphasis added). Since a is a private name in A, any reference to it outside of A is illegal.

Carbohydrate answered 28/3, 2012 at 12:32 Comment(12)
It is actually not illegal, an exception is added later for explicit template instantiations. The "all" there is obviously misleading, it's "all, unless otherwise specified". I won't downvote because it's quite unintuitive.Cacilie
@Cacilie Actually, it's not clear, and I think a DR is in order. In the standard, "all" means "all", not "all, unless otherwise specified", and the context in §14.7.2 can allow other interpretations (although they aren't very natural). This looks like a contradiction, which means that a DR is in order.Carbohydrate
You're right, there's no need for that qualification to be there.Cacilie
While you are correct (IMO) to point out this contradiction in the spec, that does not mean that "The code is clearly illegal.". It means, like for any serious legalese "if any rule of this law turns out to be defective or illegal, it is to be replaced by a rule that fits the intended purpose as good as possible." (severability clause ). You cannot take a rule and based on a contradiction interpret it contradictory to the committee's intent.Siana
You may aswell iterate over the issues of the current-issues page of wg21 and reject literally any program because of not having applied reasonable fixes of defective rules in the Standard.Siana
@JamesKanze: I was planning to submit a DR asking for cross-referencing. If you submit one for the contradiction, please let me know as I don't want to submit a duplicate.Lithotomy
@MatthieuM. I think it may mean to say something different than what we thought it means. "name" has a dual meaning in the spec. One meaning is the identifier that is declared into a declarative region. That identifier (or operator-funtion-id, literal-operator-id, etc) is thereafter called "name". For example, a member declaration declares a member-name of its class. A class declaration declares a class-name in the surrounding scope and into itself (injected-class-name). The other meaning of "name" is an identifier, operator-function-id etc.. that appears in expressions or declarations.Siana
Now note what it precisely says: "Access control is applied uniformly to all names, whether the names are referred to from declarations or expressions.". If it talked about the second meaning of name (we until now thought that it does), then "... are referred to from declarations or expressions" would not make any sense. You cannot refer to a declaration or expression from itself! Hence it talks about the first meaning of "name". For example, a member's name may be referred to by a id-expression in a class-member access expression.Siana
Actually, when read in context of 11/4 it seems to be very clear that it talks about meaning #1 of "name".Siana
@JohannesSchaub-litb: Ah thanks... I found those "term of the art" difficult to spot, but if they overload them... Well, in this case this would not be a contradiction, but it would still be good (I think) to cross-reference the section, for those of us poor readers trying to figure out the rules.Lithotomy
@JohannesSchaub-litb When I wrote "clearly illegal", I wasn't aware of the contradicting text in §14.7.2/12. Knowing this, I'd probably write that it is "illegal under the most reasonable interpretation of the standard, but the standard needs clarification here." The intent of §11/4 is obvious; the intent of §14.7.2/12 is less clear.Carbohydrate
@james as the author of class A, how would you otherwise write the explicit instantiation without violating access?Siana

© 2022 - 2024 — McMap. All rights reserved.