Calli is not Verifiable

This entry probably doesn't pertain to very many people, but I got bit by this the other day, and thought I'd at least document it to prevent some future google user from having the same problem.

In the ECMA CLI specification, the calli instruction is documented in Partition III, section 3.20.  The instruction requires the CLR to perform an indirect method call through a function pointer located on top of the stack.  This pointer must be to native code for the machine that is being executed on (which can be obtained either by the ldftn or ldvirtftn opcodes, or by being passed in from native code.  The calli opcode itself takes a method signature which indicates how many parameters the function being called expects, and what its return value is.

The section on verifiability of calli says:

“Correct CIL requires that the function pointer contains the address of a method whose signature matches that specified by callsitedescr and that the arguments correctly correspond to the types of the destination function's parameters.

Verification checks that ftn is a pointer to a function generated by ldftn or ldvirtftn.”

From looking at that, the following code snippet should compile and run just fine:

.method public static void Callee()
{
  .maxstack 1

  ldstr "In Callee()"
  call void [mscorlib]System.Console::WriteLine(string)
  ret
}

.method public static void Main()
{
  .entrypoint
  .maxstack 1

  ldftn void CalliVerif::Callee()
  calli void ()
  ret
}

And it does, as long as its operating under full trust.  Running this from the local machine with the default security policy results in the message “In Callee()” being displayed on the screen.  However, when you run this code under partial trust, you get a verification exception.  Running peverify on the assembly shows the culprit:

CalliVerif.il(27) : [IL]: Error: [CalliVerif.exe : CalliVerif::Main][offset 0x00000006] Instruction can not be verified.
1 Error Verifying CalliVerif.exe

The instruction at offset 0x6 in the Main method is the calli instruction.  Which is unexpected, since the code above follows all the verification rules:

  • Correct CIL requires that the function pointer contains the address of a method whose signature matches that specified by callsitedescr : check
  • and that the arguments correctly correspond to the types of the destination function's parameter : check
  • Verification checks that ftn is a pointer to a function generated by ldftn or ldvirtftn : check

So what's going on here?  It turns out that the CLR is not ECMA compliant in regards to the calli opcode.  Instead of allowing it to be used in verifiable code when the above three conditions are met, calli will always create unverifiable code.  This shouldn't impact too many people, since the C# and VB compilers won't generate calli instructions, but for compiler writers and others who play around in IL, it's a good bit of information to have.