• Feeds

    Subscribe in a reader

  • Ads

The mystery of the disappearing attributes

There were some good ideas in the comments on my Random Reflection Trivia post. Wilco had some good thoughts about mangled names (having both MyAttribute and MyAttributeAttribute defined in a project) as well as type forwarding...both of which were very reasonable explanations but not quite what I was getting at.

So...enough mystery. What was the magic line of config that made everything break? It was this:

     <trustLevel="Medium">

Yes, this is related to partial trust and the joys of [AllowPartiallyTrustedCallers] (a.k.a. APTCA).

In my situation, I had an attribute (MyAttribute) defined in a fully-trusted but non-APTCA assembly. This attribute was applied to a type (Foo) defined in a partially trusted assembly. As a rule, partial trust can't call into full trust unless that assembly has the APTCA bit set. And this applies to attribute constructors and property setters in the same way it applies to any other code.

When I ran into this originally, I had a brilliant idea -- why not just assert FullTrust before making the call to IsDefined()? Guess what...it still didn't work.

It turns out that the framework's implementation of GetCustomAttributes() will push a fake stack frame onto the CAS evaluation stack prior to instantiating the attribute type. This stack frame contains the security context of the assembly containing the type to which the attribute was applied. This appears before the stack frame containing the full trust assert, and as such the CAS stack walk will result in insufficient permissions. The net effect is that custom attribute constructors always run in the security context of the type to which they were applied. Asserting full trust from calling code won't get around this.

After thinking about it for a while, that behavior makes some degree of sense to me. What I don't understand is the rationale behind swallowing the resultant SecurityException. The current implementation just happily carries on -- returning false from IsDefined() and an empty array from GetCustomAttributes().

But it is what it is.