Transparency and Member Visibility
Before PDC we were talking a bit about security transparency, namely what it is and how to use it. We learned the restrictions placed on transparent code which prevents it from elevating the permissions of the call stack, namely:
- Transparent code cannot Assert for permissions to stop the stack walk from continuing.
- It cannot satisfy a LinkDemand.
- Unverifiable code causes a demand for UnmanagedCode, even if it the transparent code has SkipVerification.
- P/Invoke methods that have been decorated with the SuppressUnmanagedCodeAttribute will cause a full demand for UnmanagedCode.
In addition to these restrictions, transparent code also has a restricted view of critical tokens. Essentially, transparent code cannot see non-public critical code. This rule sits on top of normal CLR visibility rules -- we won't increase the number of tokens visible to transparent code, only decrease it. For instance, a transparent method cannot call the getter of an internal property in an assembly that contains both critical and transparent code. If it did attempt to do this, the CLR would throw a MethodAccessException.
It might be the case that there are certain non-public items that you want your transparent code to be able to see. The CLR does allow you to suppress this extra visibility check on a token by token basis by applying the System.Security.SecurityTreatAsSafeAttribute to the item you want to remain visible.
Lets look at a couple of examples:
[assembly: SecurityCritical]
public class A
{
public void Foo()
{
// transparent
Console.WriteLine(B.Baz);
}
}
[SecurityCritical(SecurityCriticalScope.Everything)]
public class B
{
internal static string Baz
{
get { /* critical */ return "baz";}
}
}
This code will throw a MethodAccessException for B.get_Baz when A.Foo gets JITed. In order to make it work, we simply apply the SecurityTreatAsSafeAttribute to B.Baz:
[assembly: SecurityCritical]
public class A
{
public void Foo()
{
// transparent
Console.WriteLine(B.Baz);
}
}
[SecurityCritical(SecurityCriticalScope.Everything)]
public class B
{
internal static string Baz
{
[SecurityTreatAsSafe]
get { /* critical, treat as safe */ return "baz";}
}
}
Now the code runs successfully, since the SecurityTreatAsSafeAttribute allows A.Foo to see B.Baz.