Reputation: 7567
Can anyone explain when using an anonymously hosted dynamic method why I get an unverifiable exception by ldvirtftn for a public virtual method on a public class? I set the following assembly level attributes as well:
[assembly: SecurityTransparent]
[assembly: SecurityRules(SecurityRuleSet.Level2,SkipVerificationInFullTrust=true)]
Here is the example code:
public class Program
{
public virtual void Foo() {}
public static void Main(string[] args)
{
Action<ILGenerator> genfunc = il => il
.newobj<Program>()
.ldvirtftn(typeof(Program).GetMethod("Foo"))
.ret();
try
{
Console.WriteLine(CodeGen.CreateDelegate<Func<IntPtr>>(genfunc).Invoke());
}
catch (System.Security.VerificationException) { }
Console.WriteLine(CodeGen.CreateDelegate<Func<IntPtr>>(genfunc,owner:typeof(Program)).Invoke());
}
}
If the method is owned, then it does not throw an exception.
Even more curious is that if I change the code like so then both methods compile and run without issue:
public class Program
{
public virtual void Foo() {}
public static void Main(string[] args)
{
Action<ILGenerator> genfunc = il => il
.newobj<Program>()
.dup()
.ldvirtftn(typeof(Program).GetMethod("Foo"))
.newobj<Action>(typeof(object),typeof(IntPtr))
.ret();
try
{
Console.WriteLine(CodeGen.CreateDelegate<Func<Action>>(genfunc).Invoke());
}
catch (System.Security.VerificationException) { }
Console.WriteLine(CodeGen.CreateDelegate<Func<Action>>(genfunc,owner:typeof(Program)).Invoke());
}
}
This code was written with the reflective library.
CodeGen.CreateDelegate simply uses the type parameter to determine the signature of the dynamic method. Here is the method::
public static TDelegate CreateDelegate<TDelegate>(
Action<ILGenerator> genfunc, string name = "", object target = null, Type owner = null, bool skipVisibility = false)
where TDelegate : class
{
var invokeMethod = typeof(TDelegate).GetMethod("Invoke");
var parameters = invokeMethod.GetParameters();
var paramTypes = new Type[parameters.Length + 1];
paramTypes[0] = typeof(object);
parameters.Select(p => p.ParameterType).ToArray().CopyTo(paramTypes, 1);
var method = owner != null ?
new DynamicMethod(name, invokeMethod.ReturnType, paramTypes, owner, skipVisibility) :
new DynamicMethod(name, invokeMethod.ReturnType, paramTypes, skipVisibility);
genfunc(method.GetILGenerator());
return method.CreateDelegate(typeof(TDelegate), target) as TDelegate;
}
Upvotes: 2
Views: 591
Reputation: 55184
Short answer
The code that you're trying to emit is unverifiable, and anonymously hosted dynamic methods can never contain unverifiable IL. Since dynamic methods associated with a type or module can contain unverifiable IL (subject to appropriate security checks), your code is usable from those dynamic methods.
Mode details
Despite MSDN's documentation, the ldvirtftn
does not load a native int onto the stack; it loads a method pointer. Just as treating an object reference as a native int is valid but unverifiable, treating a method pointer as a native int is also valid but unverifiable. The easiest way to see this is to create an assembly on disk with the same IL instructions (e.g. by using System.Reflection.Emit or ilasm) and run PEVerify on it.
I believe that the only verifiable uses of method pointers are:
dup; ldvirtftn; newobj
or ldftn; newobj
patterns to create a new delegate of a compatible delegate typecalli
with compatible arguments to make an indirect call through the method pointerThis explains why your other code can be called through an anonymously hosted dynamic method: the delegate creation pattern you are using is one of the few verifiable uses of a method pointer.
Upvotes: 4
Reputation: 16981
Strange behaviour (IntPtr != IntPtr):
//work normal
public static void F_Ldvirtftn_Action()
{
Action<ILGenerator> genfunc = il =>
{
il.Emit(OpCodes.Newobj, typeof(Program).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldvirtftn, typeof(Program).GetMethod("Foo2"));
il.Emit(OpCodes.Newobj, typeof(Action).GetConstructor(new[] { typeof(object), typeof(IntPtr) }));
il.Emit(OpCodes.Ret);
};
Console.WriteLine(CreateDelegate<Func<object>>(genfunc).Invoke());
}
// failed: VerificationException: Operation could destabilize the runtime
public static void F_IntPtr_Action()
{
Action<ILGenerator> genfunc = il =>
{
il.Emit(OpCodes.Newobj, typeof(Program).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Call, typeof(Program).GetMethod("Ptr"));
il.Emit(OpCodes.Newobj, typeof(Action).GetConstructor(new[] { typeof(object), typeof(IntPtr) }));
il.Emit(OpCodes.Ret);
};
Console.WriteLine(CreateDelegate<Func<object>>(genfunc).Invoke());
}
// failed: VerificationException: Operation could destabilize the runtime
public static void F_Ldvirtftn_MyAction()
{
Action<ILGenerator> genfunc = il =>
{
il.Emit(OpCodes.Newobj, typeof(Program).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldvirtftn, typeof(Program).GetMethod("Foo2"));
il.Emit(OpCodes.Newobj, typeof(MyAction).GetConstructor(new[] { typeof(object), typeof(IntPtr) }));
il.Emit(OpCodes.Ret);
};
Console.WriteLine(CreateDelegate<Func<object>>(genfunc).Invoke());
}
//work normal
public static void F_IntPtr_MyAction()
{
Action<ILGenerator> genfunc = il =>
{
il.Emit(OpCodes.Newobj, typeof(Program).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Call, typeof(Program).GetMethod("Ptr"));
il.Emit(OpCodes.Newobj, typeof(MyAction).GetConstructor(new[] { typeof(object), typeof(IntPtr) }));
il.Emit(OpCodes.Ret);
};
Console.WriteLine(CreateDelegate<Func<object>>(genfunc).Invoke());
}
public static IntPtr Ptr(object z)
{
return IntPtr.Zero;
}
public class MyAction
{
public MyAction(object z, IntPtr adr) { }
}
Upvotes: 0
Reputation: 171178
ldvirtfn loads a native int onto the stack. I think you need to convert that to IntPtr first before returning.
Upvotes: 0