Using Lightweight CodeGen from Partial Trust

Last time I talked about the new Orcas feature allowing you to use reflection from partial trust.  Specifically we talked about standard reflection and Reflection.Emit, putting off Lightweight CodeGen until today.

Before we start, if you're new to LCG, you might want to check out Yiru's quick introduction to the feature.  If you're planning on doing much work with DynamicMethods, Haibo's excellent VS 2005 DynamicMethod visualizer is highly recommended.

Traditional Dynamic Methods

The v2.0 DynamicMethod constructors took as a parameter either a module or type to host the method.  In Orcas, these constructors will no longer demand ReflectionPermission/ReflectionEmit to use.  If you've selected to host the dynamic method outside of your assembly, then a demand for ReflectionPermission/RestrictedMemberAccess + the grant set of the target assembly will be done.  For compatibility with v2.0, if a demand for SecurityPermission/ControlEvidence would have succeeded then this operation is also allowed.  Using the skip visibility feature will still require ReflectionPermission/MemberAccess.

Let's look at a few examples to help clear that up.  Lets say a simple sandbox domain is setup with a grant set of Internet + RestrictedMemberAccess.  Assemblies A and B containing types TypeA and TypeB are loaded into the domain, along with two host assemblies, HostAssemblyA and HostAssemblyB containing HostTypeA and HostTypeB.

Emitting type Host target Success Reason
TypeA TypeA Yes You can always emit into your own assembly
TypeA TypeB Yes Everything in the domain has RMA + the grant set of Assembly A (so RMA + Internet)
HostTypeA TypeB Yes Everything in the domain has RMA + the grant set of Assembly B
TypeB HostTypeB No In this case, the demand will be for RMA + grant of HostAssembly = FullTrust. This will fail on B's stack frame
HostTypeA HostTypeB It depends :-) This will also trigger a demand for FullTrust, which will be satisfied by HostTypeA but fail when it hits the AppDomain boundary. If HostTypeA asserted for FullTrust before creating the dynamic method this will succeed, otherwise it will fail.

This all boils down to one rule which is very similar to the rule for partial trust standard reflection, specifically you can emit methods into assemblies within your trust level -- however you can not emit methods into assemblies which have more trust than you.

Anonymously Hosted Dynamic Methods

Orcas introduces a new set of constructors which do not allow you to specify the location where the DynamicMethod is hosted and are specially tailored to enable partially trusted code to use LCG -- no demands are made when emitting one of these anonymously hosted dynamic methods.

One of the constructors for anonymously hosted dynamic methods accept a restricted skip visibility flag.  If you create your method and do not ask for restricted skip visibility then everything works exactly as you would expect.  Namely, your new method can access public members of any type to do its work. 

Things are much more interesting when restricted skip visibility is set to true.  Generally, restricted skip visibility is the partial trust equivalent of the skip visibility parameter on standard dynamic methods, meaning that the JIT does not do access checks when your method attempts to access various types, fields, methods and properties.  This allows dynamically generated code to access private and internal members of types that it would not normally have access to -- which obviously needs to be a protected operation.

If restricted skip visibility is on, then each time a member is accessed that a normal visibility check would prevent, a demand for ... you guessed it, RestrictedMemberAccess + the permissions of the target is done.  This demand is done against the call stack which was in place when the anonymously hosted dynamic method was created -- which may or may not be the same as the call stack when it gets JITed.

Another few examples of restricted skip visibility are probably in order.  We'll use the same set of types and assemblies as the above examples:

Let's say that TypeA creates an anonymously hosted dynamic methods with restricted skip visibility that accesses a private field of TypeB.  This dynamic method is later invoked:

Construction call stack      JIT call stack
HostTypeA.InvokeAddIn FullTrust TypeA.OnAddInInvoked Internet + RMA
TypeA.Initialize Internet + RMA TypeA.RunDynamicMethod Internet + RMA
TypeA.EmitDynamicMethod Internet + RMA (DynamicMethod)

When B causes the dynamic method to be JITed, we see an access to a private member of B.  Since B has a grant set of Internet + RMA, the resulting demand is also Internet + RMA.  This demand is done against the dynamic method construction call stack and succeeds since everybody on the stack has at least Internet + RMA.

In a similar example, the host assemblies might want to access private members of each other.  For instance if HostTypeA emits a dynamic method which accesses an internal type in HostAssemblyB:

Construction call stack      JIT call stack
HostTypeA.Initialize FullTrust TypeA.OnAddInInvoked Internet + RMA
(Assert FullTrust) TypeA.RunDynamicMethod Internet + RMA
HostTypeA.EmitDynamicMethod FullTrust (DynamicMethod) FullTrust (transparent)

This will also succeed.  The JIT will do a demand for RMA + the grant set of the internal type in HostAssemblyB, which becomes a demand for FullTrust.  Even though the dynamic method is being JITed and invoked on a call stack where nothing is fully trusted, the demand succeeds because it goes against the call stack when the dynamic method was constructed, which was entirely fully trusted.  The assert was necessary to prevent the demand from hitting the partially trusted AppDomain boundary.

Three Simple Rules for Partial Trust LCG

Although the rules for using LCG from partial trust can seem complicated at first, they really boil down to these three main points:

  1. If RMA is granted, you can emit into any assembly at your trust level or lower
  2. Any partial trust code can emit an anonymously hosted dynamic method
  3. If RMA is granted, your anonymously hosted dynamic method can also use restricted skip visibility to skip JIT time visibility checks against members at your trust level or lower