val*_*ano 5 .net c# cil invalidprogramexception
我有以下一段简化的CIL代码.
执行此CIL方法时,CLR将抛出InvalidProgramException:
.method assembly hidebysig specialname rtspecialname
instance void .ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1<class System.Windows.Input.StylusDeviceBase> styluses) cil managed
{
.locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1<class System.Windows.Input.StylusDeviceBase> V_0,
class System.Windows.Input.StylusDeviceBase V_1)
ldc.i4.8 // These instructions cause CIL to break
conv.u //
localloc //
pop //
ldarg.0
newobj instance void class [mscorlib]System.Collections.Generic.List`1<class System.Windows.Input.StylusDevice>::.ctor()
call instance void class [mscorlib]System.Collections.ObjectModel.ReadOnlyCollection`1<class System.Windows.Input.StylusDevice>::.ctor(class [mscorlib]System.Collections.Generic.IList`1<!0>)
ldarg.1
callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<class System.Windows.Input.StylusDeviceBase>::GetEnumerator()
stloc.0
.try
{
leave.s IL_0040
}
finally
{
endfinally
}
IL_0040: ret
} // end of method StylusDeviceCollection::.ctor
Run Code Online (Sandbox Code Playgroud)
我的问题是,为什么这个CIL代码无效?
几个obervations:
- 如果localloc删除,代码运行正常.据我所知,localloc用一个地址替换堆栈上的参数大小,因此堆栈保持平衡,AFAICT.
- 如果删除了try和finally块,代码运行正常.
- 如果第一个包含的指令块localloc被移动到try-finally块之后,则代码运行正常.
所以它似乎是localloc和try-finally组合的东西.
一些背景:
由于在运行时进行了一些检测,因此在为原始方法抛出InvalidProgramException之后我达到了这一点.为了弄清楚仪器的问题,我调试它的方法是:
ildasm ilasm 不幸的是,peverify.exe /IL没有表明任何错误.我试图安装ECMA规范和Serge Lidin的专家.NET IL书籍,但无法弄清楚它出了什么问题.
我有什么基本的东西吗?
编辑:
我略微更新了有问题的IL代码,使其更加完整(无需修改指令).的指令,包括第二块ldarg,newobj等等,被取为是从工作代码-原来的方法的代码.
对我来说奇怪的是,通过删除localloc或者.try- finally,代码可以工作 - 但据我所知,这些都不应该改变堆栈的平衡,相比之下它们是否存在于代码中.
这是用ILSpy反编译成C#的IL代码:
internal unsafe StylusDeviceCollection(IEnumerable<StylusDeviceBase> styluses)
{
IntPtr arg_04_0 = stackalloc byte[(UIntPtr)8];
base..ctor(new List<StylusDevice>());
IEnumerator<StylusDeviceBase> enumerator = styluses.GetEnumerator();
try
{
}
finally
{
}
}
Run Code Online (Sandbox Code Playgroud)
编辑2:
更多观察:
- 获取locallocIL代码块,并将其移动到函数末尾,代码运行正常 - 所以看起来代码本身就可以了.
- 将类似的IL代码粘贴到hello world测试函数中时,问题不会重现.
我很困惑......
我希望有一种方法可以从InvalidProgramException获取更多信息.似乎CLR没有将确切的失败原因附加到异常对象.我还想过使用CoreCLR调试版本进行调试,但不幸的是我正在调试的程序与它不兼容......
可悲的是,我似乎遇到了 CLR 错误......
使用旧版 JIT 编译器时一切正常:
set COMPLUS_useLegacyJit=1
我无法隔离可能导致此问题的特定 RyuJit 设置。我遵循了本文中的建议:
https ://github.com/Microsoft/dotnet/blob/master/Documentation/testing-with-ryujit.md
感谢所有提供帮助的人!
后果:
在我遇到遗留 JIT 解决方法之后的某个时间,我意识到这个问题仅在插入localloc(这是不可验证的操作码)到从安全透明方法调用的安全关键方法中时才会出现。仅在这种情况下,RyuJit 才会抛出InvalidProgramException,而 Legacy JIT 不会。
在我的重现中,我反汇编并重新组装了有问题的 DLL,并直接修改了函数代码,保持安全属性完整 - 特别是AllowPartiallyTrustedCallers程序集属性 - 这解释了为什么没有用一个孤立的示例重现该问题。
与旧版 JIT 相比,RyuJIT 可能有一些安全强化,这导致了这个问题,但事实上,在存在 try-catch 及其相对位置的情况下,locallocCLR 会抛出一个依赖项,看起来确实像一个微妙的错误。InvalidProgramExceptionlocalloc
在失败的 DLL 上运行SecAnnotate.exe(.NET 安全注释器工具)有助于揭示函数调用之间的安全问题。
有关安全透明代码的更多信息:
https://learn.microsoft.com/en-us/dotnet/framework/misc/security-transparent-code