Почему Ldvirtftn не поддается проверке? - PullRequest
2 голосов
/ 01 апреля 2012

Может ли кто-нибудь объяснить при использовании динамического метода, размещенного анонимно, почему я получаю непроверяемое исключение ldvirtftn для общедоступного виртуального метода в общедоступном классе?Я также установил следующие атрибуты уровня сборки:

[assembly: SecurityTransparent]
[assembly: SecurityRules(SecurityRuleSet.Level2,SkipVerificationInFullTrust=true)]

Вот пример кода:

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());
    }
}

Если метод принадлежит, то он не выдает исключение.

Еще более любопытно, что если я изменю код таким образом, то оба метода компилируются и запускаются без проблем:

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());
    }
}

Этот код был написан с использованием отражающей библиотеки.

CodeGen.CreateDelegate просто использует параметр типа для определения сигнатуры динамического метода.Вот метод ::

    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;
    }

Ответы [ 3 ]

4 голосов
/ 03 апреля 2012

Краткий ответ

Код, который вы пытаетесь выдать, не поддается проверке, а динамические методы, размещенные на анонимном хосте, никогда не могут содержать непроверяемый IL. Поскольку динамические методы, связанные с типом или модулем, могут содержать непроверяемый IL (при условии соответствующих проверок безопасности), ваш код можно использовать из этих динамических методов.

Детали режима

Несмотря на документацию MSDN, ldvirtftn не загружает собственный int в стек; он загружает указатель метода. Так же, как обработка ссылки на объект как нативного int действительна, но не поддается проверке, так и обработка указателя на метод как нативного int также допустима, но не поддается проверке. Самый простой способ убедиться в этом - создать сборку на диске с теми же инструкциями IL (например, с помощью System.Reflection.Emit или ilasm) и запустить на нем PEVerify.

Я считаю, что единственными проверяемыми видами использования указателей методов являются:

  • Построение делегата с использованием шаблонов dup; ldvirtftn; newobj или ldftn; newobj для создания нового делегата совместимого типа делегата
  • Использование calli с совместимыми аргументами для косвенного вызова через указатель метода

Это объясняет, почему ваш другой код может вызываться через размещенный в анонимном режиме динамический метод: используемый вами шаблон создания делегата является одним из немногих проверяемых применений указателя метода.

0 голосов
/ 01 апреля 2012

Странное поведение (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) { }
}
0 голосов
/ 01 апреля 2012

ldvirtfn загружает собственный int в стек. Я думаю, вам нужно сначала преобразовать это в IntPtr, прежде чем возвращать.

...