7

在验证一些包含返回指针的不安全方法的代码时,我遇到了这个问题。

该示例可以表示为:

public class A
{
    public static unsafe int* GetAnswer()
    {
        int fakeValue = 42;
        return &(fakeValue);
    }

    public static void Main()
    {
        int i = 0;
        unsafe { i = *A.GetAnswer(); }
        System.Console.WriteLine(i);
    }
}

我正在使用两个独立的验证工具,即 ILVerify 和 Peverify。

重现步骤:

  1. 使用编译示例代码csc example.cs /t:library /unsafe
  2. 核实peverify example.dll
  3. 核实ILVerify.exe -r C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorlib.dll example.dll

2. 和 3. 都将导致以下错误消息:

[IL]:错误:[C:\src\test\example.dll : A::GetAnswer()][offset 0x00000006][found address of Int32] 堆栈上的预期数字类型。

[IL]:错误:[C:\src\test\example.dll : A::Main()][offset 0x00000009][found Native Int] 堆栈上需要 ByRef。2 验证 C:\src\test\example.dll 的错误

神秘的是,一切都按预期编译和运行,它不会验证。有没有人对为什么会这样有一些洞察力?

4

1 回答 1

7

从根本上说:不安全的代码是无法验证的。您返回的确切消息通常会含糊不清,但话又说回来:不安全代码(badum tsh)也是如此!

更糟糕的是:问题中的代码被主动破坏 - 当您从已退出的堆栈帧访问指针时,没有定义的行为。在这种情况下,您通常会侥幸逃脱并看到最后的值,但是:它没有被定义。

如果你想要可验证的代码,你需要切换到ref return; 例如:

static ref int GetAnswer(int[] arr)
{
    return ref arr[0];
}

static void Main()
{
    int i = 0;
    int[] j = new int[] { 42 };
    i = A.GetAnswer(j);
    System.Console.WriteLine(i);
}

这不使用不安全的代码。GetAnswer返回对数组中第一个元素的引用(不是第一个元素的) - 作为托管指针(ref T是托管指针;T*是非托管指针)。分配i = {someRef}(而不是i = ref {someRef}尊重托管指针,就像i = *{somePtr}非托管指针一样。

这清楚地验证了:

Microsoft (R) .NET Framework PE Verifier.  Version  4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

All Classes and Methods in ConsoleApp35.exe Verified.
于 2018-01-22T14:23:32.017 回答