RestrictedMemberAccess

The September CTP of Orcas went live last night, including lots of features that other MSDN blogs are buzzing about such as LINQ to Objects, partial C# 3.0 support, and partial VB 9.0 support. (And prompting me to create the new Orcas category to replace the defunct Whidbey category).

However, my favorite feature available in the CTP is down at the bottom of the description:

Reflection in Partial Trust, enabling sand box scenarios for all applications that depend on these features

Yep, reflection is now available in partial trust!

So, what exactly is this feature?  It's easier to consider how it affects the three major portions of reflection:

  1. Reflection.Emit
  2. Reflection
  3. DynamicMethods

Reflection.Emit is the easiest of the group.  ReflectionPermission/ReflectionEmit is no longer demanded when you try to emit an assembly.  If you try to write the assembly to disk obviously you need FileIOPermission (and for obscure reasons ReflectionEmit to write out debug symbols), but a standard in-memory assembly can be emitted by any assembly with any permission set.  The newly emitted assembly will run with the same permissions as the assembly which emitted it.

Standard reflection is also relatively straight forward.  There is now a new flag on ReflectionPermission, RestrictedMemberAccess, which should be granted to any assembly which needs to have access to reflection in partial trust.  When some partial trust code attempts to use reflection to access a member which it would not be able to access via standard visibility rules, the CLR will now demand the grant set of the target object plus RestrictedMemberAccess.

For instance, assembly A and B are both part of the same AddIn, and are hosted in a simple sandbox domain granting them the Internet permission set and RestrictedMemberAccess.  If a method in assembly A tries to use reflection to invoke a private method in assembly B, the CLR will demand:

Target Permissions union RestrictedMemberAccess =
B's permissions union RestrictedMemberAccess =
Internet + RestrictedMemberAccess

The call stack at this point might look like this:

AppDomain Boundary Internet + RestrictedMemberAccess
HostAssembly AddInManager.LaunchAddIn FullTrust
A AddIn.Run Internet + RestrictedMemberAccess
A AddIn.InvokePrivateMethodOnB Internet + RestrictedMemberAccess

As the demand for Internet + RestrictedMemberAccess proceeds up the call stack, it will succeed at each frame, and therefore A will be allowed to invoke a private method in B.

Now, suppose A tries to invoke an internal member in an assembly that's part of the host application, say Host.EraseAllUserData().  In this case, the CLR will demand:

Target Permissions union RestrictedMemberAccess =
Host assembly permissions union RestrictedMemberAccess =
FullTrust union RestrictedMemberAccess =
FullTrust

By looking at the call stack above, we can see that the demand for FullTrust will fail as soon as it hits the first frame, since A is not fully trusted.

The reverse scenario will succeed ... if the host assembly tries to reflect into A, the demand for Internet + RestrictedMemberAccess will again succeed at all points on the call stack, allowing the host access to both A and B.  (Notice though that if a host assembly attempts to reflect on private members of another fully trusted assembly it would have to Assert for FullTrust first, since the resulting demand would fail when it hit the AppDomain boundary).

What this boils down to is that any assembly granted RestrictedMemberAccess will be able to reflect on any assembly granted either the same set of permissions it has or a subset thereof.  For compatibility reasons, reflection demands will also succeed if the call stack was granted MemberAccess.

Next time, partial trust reflection and dynamic methods ... until then, have fun playing around with the CTP over the weekend! :-) [By the way, this is even easier to do now, since the download page has included Virtual PC images with the CTP already installed].