I have run into this issue when verifying some code containing an unsafe method that returns a pointer.
The example can be expressed as this:
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);
}
}
I am using two separate verification tools, namely ILVerify and Peverify.
Steps to reproduce:
csc example.cs /t:library /unsafe
peverify example.dll
ILVerify.exe -r C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorlib.dll example.dll
Both 2. and 3. will result in the error message below:
[IL]: Error: [C:\src\test\example.dll : A::GetAnswer()][offset 0x00000006][found address of Int32] Expected numeric type on the stack.
[IL]: Error: [C:\src\test\example.dll : A::Main()][offset 0x00000009][found Native Int] Expected ByRef on the stack. 2 Error(s) Verifying C:\src\test\example.dll
The mystery is that everything compiles and runs as expected, it will not verify. Does anyone have some insight knowledge about why this is the case?
Fundamentally: unsafe code is unverifiable. The exact messages you get back will often be vague and confusing, but then again: so is unsafe code (badum tsh)!
Worse: the code in the question is actively broken - there is no defined behaviour for what happens when you access a pointer from a stack-frame that has exited. In this case you'll usually get away with it and see the last values, but: it isn't defined.
If you want verifiable code, you're going to need to switch to ref return
; for example:
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);
}
This uses no unsafe code. GetAnswer
returns a reference to the first element in the array (not the value of the first element) - as a managed pointer (ref T
is a managed pointer; T*
is an unmanaged pointer). Assigning i = {someRef}
(rather than i = ref {someRef}
) deferences the managed pointer, exactly like i = *{somePtr}
does for an unmanaged pointer.
This verifies cleanly:
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.
User contributions licensed under CC BY-SA 3.0