2

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;
    }
Michael B
  • 7,512
  • 3
  • 31
  • 57
  • MSIL is not just for managed code. You can compile raw native C++ code to IL as well. Done by the C++/CLI compiler. The kind of code where Opcodes.Ldvirtfn is important, it digs a function pointer out of a v-table. A raw address, IntPtr, not a heckofalot of verification is possible when the jitter verifier is hit with that. – Hans Passant Mar 31 '12 at 22:04
  • True, but then why does giving the method an owning type make the verification issue go away? – Michael B Mar 31 '12 at 22:36

3 Answers3

4

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:

  • Constructing a delegate using the dup; ldvirtftn; newobj or ldftn; newobj patterns to create a new delegate of a compatible delegate type
  • Using calli with compatible arguments to make an indirect call through the method pointer

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

kvb
  • 54,864
  • 2
  • 91
  • 133
  • PEVerify considers calli to ALWAYS be unverifiable (which makes sense) of course. http://blogs.msdn.com/b/shawnfa/archive/2004/06/14/155478.aspx Very strange but completely believable. – Michael B Apr 03 '12 at 00:22
  • @MichaelB - good point, I listed the rules that the ECMA spec covers, which is not the same as what PEVerify actually implements. – kvb Apr 03 '12 at 04:32
0

ldvirtfn loads a native int onto the stack. I think you need to convert that to IntPtr first before returning.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Calling conv_i or conv_u doesn't make this puppy workable. You may be right that it is a return type issue. A verification issue is also presented if you a function that returns uint returns an actual int and don't convert it first. (e.g. ldc_m1 ret) – Michael B Mar 31 '12 at 22:39
  • As an emergency measure you could conv.i8 and use the IntPtr(long) overload. – usr Apr 02 '12 at 22:18
0

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) { }
}
Serj-Tm
  • 16,581
  • 4
  • 54
  • 61