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].

Comments (3)

  1. Jamebo says:

    HI, Shawn:

    Thank you for your great post to explain the details. I faced a problem in RestrictedMemberAccess flag.

    Case 1:

    Appdomain Boundary (MediumTrust + RestrictedMemberAccess ) -> Assembly A with FullTrust (in GAC)

    -> Assembly B with MediumTrust + RestrictedMemberAccess

    The result is okay as RestrictedMemberAccess described.

    Case 2:

    Install Assembly B into GAC, then it is:

    Appdomain Boundary (MediumTrust + RestrictedMemberAccess ) -> Assembly A with FullTrust (in GAC)

    -> Assembly B with Full Trust (in GAC)

    An Security exception will be thrown : Demand falied for

    <PermissionSet class="System.Security.PermissionSet"version="1" Unrestricted="true"/>.

    Why? In case 2, Assembly B has same grant set with Assembly A. It should be okay for RestrictedMemberAccess. Could you please give me some comments?

    Thanks

    -Jamebo

  2. shawnfa says:

    Hi Jamebo,

    What you’re running into is that the demand triggered when B reflects onto A is for A’s grant set + RestrictedMemberAccess.  That works out to FullTrust + RestrictedMemberAccess = FullTrust.

    The full trust demand will not fail when it sees B’s frame on the stack, as you correctly noted B is in the GAC and fully trusted.  However, when the demand continues up the stack it will eventually hit the AppDomain boundary.  The domain itself is medium trust, so the demand for full trust fails there.

    What you would need to do in this situation is have B Assert for FullTrust before doing the reflection in order to prevent the demand from proceeding beyond B’s frame and hitting the AppDomain.

    -Shawn