9

When trying to compile an Expression in a medium trust web app I'm getting a MethodAccessException. Does anyone know of another way to compile an expression under medium trust or a workaround to avoid this exception?

The code that throws the exception:

Expression<Func<object>> efn = 
  Expression.Lambda<Func<object>>(Expression.Convert((plan,typeof(object)));

Func<object> fn = efn.Compile(); // Exception thrown here

The variable plan is an Expression that represents the following execution plan:

{
  Convert(Query(MyProjectNamespace.MyDatabaseTableObject).Provider).Execute
  (
    new QueryCommand(
    "SELECT [t0].[LinkId], [t0].[Url] FROM [dbo].[MyDatabaseTable] AS t0",
    value(System.String[]), 
    r0 => new MyDatabaseTableObject() 
    {
      Id = IIF(r0.IsDBNull(0), 0, 
        Convert(ChangeType(r0.GetValue(0), System.Int32))), 
      Url = IIF(r0.IsDBNull(1), null, 
        Convert(ChangeType(r0.GetValue(1), System.String)))
    }, 
    value(System.Collections.Generic.List[System.String])), 
    new [] {}
  )
}

The full stack trace:

at System.Reflection.MethodBase.PerformSecurityCheck(Object obj, RuntimeMethodHandle method, IntPtr parent, UInt32 invocationFlags)
at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
at System.Linq.Expressions.ExpressionCompiler.AddGlobal(Type type, Object value)
at System.Linq.Expressions.ExpressionCompiler.GenerateConstant(ILGenerator gen, Type type, Object value, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateConstant(ILGenerator gen, ConstantExpression c, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateArgs(ILGenerator gen, ParameterInfo[] pis, ReadOnlyCollection`1 args)
at System.Linq.Expressions.ExpressionCompiler.GenerateMethodCall(ILGenerator gen, MethodInfo mi, ReadOnlyCollection`1 args, Type objectType)
at System.Linq.Expressions.ExpressionCompiler.GenerateMethodCall(ILGenerator gen, MethodCallExpression mc, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateConvert(ILGenerator gen, UnaryExpression u)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateConditional(ILGenerator gen, ConditionalExpression b)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateMemberAssignment(ILGenerator gen, MemberAssignment binding, Type objectType)
at System.Linq.Expressions.ExpressionCompiler.GenerateBinding(ILGenerator gen, MemberBinding binding, Type objectType)
at System.Linq.Expressions.ExpressionCompiler.GenerateMemberInit(ILGenerator gen, ReadOnlyCollection`1 bindings, Boolean keepOnStack, Type objectType)
at System.Linq.Expressions.ExpressionCompiler.GenerateMemberInit(ILGenerator gen, MemberInitExpression init)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateLambda(LambdaExpression lambda)
at System.Linq.Expressions.ExpressionCompiler.GenerateCreateDelegate(ILGenerator gen, LambdaExpression lambda)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateArgs(ILGenerator gen, ParameterInfo[] pis, ReadOnlyCollection`1 args)
at System.Linq.Expressions.ExpressionCompiler.GenerateNew(ILGenerator gen, NewExpression nex, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateArgs(ILGenerator gen, ParameterInfo[] pis, ReadOnlyCollection`1 args)
at System.Linq.Expressions.ExpressionCompiler.GenerateMethodCall(ILGenerator gen, MethodInfo mi, ReadOnlyCollection`1 args, Type objectType)
at System.Linq.Expressions.ExpressionCompiler.GenerateMethodCall(ILGenerator gen, MethodCallExpression mc, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateConvert(ILGenerator gen, UnaryExpression u)
at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
at System.Linq.Expressions.ExpressionCompiler.GenerateLambda(LambdaExpression lambda)
at System.Linq.Expressions.ExpressionCompiler.CompileDynamicLambda(LambdaExpression lambda)
at System.Linq.Expressions.Expression`1.Compile()
at SubSonic.Linq.Structure.DbQueryProvider.Execute(Expression expression)
at SubSonic.Linq.Structure.QueryProvider.System.Linq.IQueryProvider.Execute(Expression expression)
at SubSonic.Linq.Structure.Query`1.GetEnumerator()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at WebApplication1._Default.Page_Load(Object sender, EventArgs e)
at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)
at System.Web.UI.Control.OnLoad(EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
Andras Zoltan
  • 41,961
  • 13
  • 104
  • 160
Adam Cooper
  • 8,077
  • 2
  • 33
  • 51
  • 1
    You do not have the `ReflectionPermission(ReflectionPermissionFlag.MemberAccess)` to access non-public members. – Jaroslav Jandek Jul 07 '10 at 13:32
  • @Jaroslav - I know that's the cause of the error I'm trying to work out what it is that's requiring that permission and how to get round it, if that's possible. – Adam Cooper Jul 08 '10 at 17:35
  • 1
    @Adam: if you use any non-public members (properties, fields, methods, constructors), try it without them (if you can). Even external parameters can cause such behaviour. Try to "reflect" around the used classes... – Jaroslav Jandek Jul 08 '10 at 21:09
  • 1
    @Jaroslav - whilst technically correct - it's actually got nothing to do with the direct use of any non-visible methods; it's a subtle issue to do with baking `Type` instances into expressions - `RuntimeType` (used for all reflected type instances at runtime) is internal, and it's causing `StrongBox` to blow. – Andras Zoltan Jul 09 '10 at 10:17
  • 2
    @Andras: Sounds likely. A quick look at the stack trace suggest it's this priece of code `ChangeType(r0.GetValue(0), System.Int32)`. Maybe using an alternative method like `ChangeTypeTo` could solve the problem - it depends on the implementation of SubSonic though. – Jaroslav Jandek Jul 09 '10 at 14:42
  • @Jaroslav: Using a generic is a good idea. I found another solution (in my answer) where instead of calling `Expression.Constant([type instance])`, you can call `Expression.Constant([type instance], typeof(Type))` so that the code-generation functions reflect over the reflection-friendly `Type` instead of `RuntimeType`. – Andras Zoltan Jul 09 '10 at 15:01

1 Answers1

15

The underlying problem here is that the type being passed to System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes) is either not public, or has a constructor that is not public.

Now - given the simplicity of your code example versus the depth of the stacktrace I believe the problem lies not in plan, but in an expression within plan (since you say in your comment on Marc's answer that it is also an expression) that references the type which is then restricted.

The expression that is the source of the error here is a ConstantExpression which must be of the restricted type.

The only confusing thing about this, however, is that the type argument that AddGlobal passes to Activator.CreateInstance is StrongBox<T>, which is public and has a public constructor - which would imply that this error should be impossible.

Perhaps, however, there is something hidden associated with StrongBox<T> that we can't see through Reflector.

So, I would be looking at the whole expression tree represented by plan and examining all the types referenced in ConstantExpressions to ensure they are all accessible. If after doing that all types are shown to be accessible, this error still occurs then it could be a bug in the framework.

However - I would have thought that such a bug would've been found already for something as simple as a ConstantExpression!

EDIT (Replacing Previous Edit) WITH THE ANSWER

I've got it, and it's a very subtle problem. You can reproduce with this little bit of code in an aspx page that is configured to run in medium trust:

Type t = typeof([any type you fancy]);
Expression expr = Expression.Constant(t);
var lambda = Expression.Lambda<Func<Type>>(expr);
var del = lambda.Compile();
Response.Write(del().ToString());

So, in the code you've provided, it's the expression representing the second argument to ChangeType (took me a while to realise that that is a Sub Sonic method), which appears to be a Type (can't see the code but I think it's a reasonable guess!).

It's baked in the expression as a ConstantExpression of a Type instance. Don't ask how I narrowed down the parameter - lots of stack crawling and reflector work ;)

As mentioned in the first half of my answer, it's difficult to see how the code that the Expression Tree compiler uses can ever create a MethodAccessException, since it's always accessing the public ctor of the StrongBox<T> type.

However, it would get upset if the type passed in as the generic is not public. "But wait," you say, "Type is public!".

That might be, but the Type instance returned at runtime from typeof() or GetType() isn't - it's an instance of RuntimeType - which is internal.

Which is also why the above code snippet will also trigger the same error.

The fix

Change the code that produces the Type argument for ChangeType(,) from

Expression.Constant([type])

(which I'll almost guarantee that it is at the moment) to

Expression.Constant([type], typeof(Type))

This works, because you're explicitly telling the compiler to use the public Type for the constant, instead of the reflected type of RuntimeType.

You can test this fix by applying it to my example code in the previous block and re-running.

Andras Zoltan
  • 41,961
  • 13
  • 104
  • 160
  • Thanks this is really helpful, do you have any suggestion on a quick way to track down the non accessible types? I'm pretty confident this isn't a framework bug but I'm finding pinpointing the exact cause a significant challenge. – Adam Cooper Jul 07 '10 at 18:26
  • Hmmm... well I considered how you could do this. Perhaps you can use an expression visitor which looks for `ConstantExpression` whose type is either not public or which has at least one non-public constructor-that should narrow it down. MSDN has a link to an example expression visitor. I'll take a look and post it up on the answer. – Andras Zoltan Jul 07 '10 at 20:19
  • @Adam - I have updated my answer with some code that might be able to help you diagnose, alongside that link from the MSDN which you'll need for an Expression Tree Visitor. It's not perfect - but it should set you on your way pretty well I hope :) – Andras Zoltan Jul 07 '10 at 20:56
  • @Andras - this is awesome thanks, I'll let you know how I get on :) – Adam Cooper Jul 08 '10 at 09:25
  • Hmmm... The only two dodgyConstants returned are System.Int32 and System.String – Adam Cooper Jul 08 '10 at 13:00
  • @Adam: oh dear, this is a little strange... The stack trace doesn't lie, so therefore perhaps this is something bigger. There are some issues with generating code in medium trust (the RestrictedMemberAccess permission mentioned by the first commenter on your question) - but the MSDN documentation on Expression Trees doesn't mention requiring anything special for to actually do anything. You could try requesting the RestrictedMemberAccess permission - I just can't see any reason why you actually need it... – Andras Zoltan Jul 08 '10 at 15:59
  • Yep, that's what I'm trying to get to the bottom of. I know I can add that permission and skip the problem but I'm trying to fix an issue in SubSonic (http://subsonicproject.com) so it can be used in medium trust. Debugging the issue is proving problematic to say the least though hence the question. Thanks for your help so far :) – Adam Cooper Jul 08 '10 at 17:33
  • I reckon we can reproduce this overall issue in a test environment... I'll have a poke about and see what I can find. – Andras Zoltan Jul 09 '10 at 07:24
  • @Adam - I've figured it out (I hope). Please see my updated answer - it's a real keeper of a problem! – Andras Zoltan Jul 09 '10 at 10:16
  • @Andras - Honestly can't thank you enough for this. Amazing detective work you're absolutely right. Thanks again :) – Adam Cooper Jul 10 '10 at 13:45
  • @Adam - not a problem - a fascinating bug and glad to be of help. Good luck with your work on Subsonic :) – Andras Zoltan Jul 10 '10 at 17:12
  • 3
    It's a damn shame this is an obscure problem, because an answer of this quality deserves more up-votes. – Mark Rendle Aug 29 '12 at 10:26