我有以下一段简化的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
我的问题是,为什么这个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
{
}
}
编辑2:
更多观察:
- 获取IL代码的localloc
块,并将其移动到函数末尾,代码运行正常 - 因此看起来代码本身就可以了。
- 将类似的IL代码粘贴到hello world测试函数中时,问题不会重现。
我很困惑......
我希望有一种方法可以从 InvalidProgramException 中获取更多信息。似乎CLR没有将确切的失败原因附加到异常对象。 我还考虑过使用CoreCLR调试版进行调试,但不幸的是我正在调试的程序与它不兼容......
答案 0 :(得分:2)
可悲的是,我似乎遇到了CLR错误......
使用旧版JIT编译器时一切正常:
set COMPLUS_useLegacyJit=1
我无法隔离可能导致此问题的特定RyuJit设置。
我遵循了本文的建议:
https://github.com/Microsoft/dotnet/blob/master/Documentation/testing-with-ryujit.md
感谢所有帮助过的人!
<强>后果:强>
在我遇到遗留JIT解决方法之后的某个时间,我意识到只有在将 在我的复制中,我反汇编并重新组装了有问题的DLL并直接修改了功能代码,保持了安全属性的完整性 -
特别是 可能是在RyuJIT中,与Legacy JIT相比,存在一些安全性问题,这表明了这个问题,但是 在发生故障的DLL上运行 SecAnnotate.exe (.NET Security Annotator tool)有助于揭示函数调用之间的安全问题。 有关安全透明代码的更多信息:localloc
(这是一个不可验证的操作码)变为从安全透明方法调用的安全关键方法时才会出现此问题< / strong>即可。只有在这种情况下,RyuJit才会抛出InvalidProgramException
,而Legacy JIT则不会。{/ p>
AllowPartiallyTrustedCallers
汇编属性 - 这解释了为什么问题没有用一个孤立的例子再现。localloc
将导致CLR在存在时抛出InvalidProgramException
depdending的事实try-catch及其与localloc
的相对位置确实看起来像一个微妙的错误。
https://docs.microsoft.com/en-us/dotnet/framework/misc/security-transparent-code