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.

Comments (3)

  1. S N says:

    Hi,

    You forgot to mention an additional condition. If the function is not a static function, then the function should be a member of this object’s type.

    Thanks

    SN

  2. Shawn says:

    Are you talking about a verifiability rule or just a correct IL rule? You can call indirectly to an instance function of another class. For instance, even though this snippet is not verifiable due to calli, the following code works as expected:

    ldstr "Test String"

    ldftn instance string [mscorlib]System.String::ToLower()

    calli instance string ()

    call void [mscorlib]System.Console::WriteLine(string)

    This will print "test string" to the console.

    Taking out the calli, will produce verifiable code as well, so it’s also possible to ldftn an instance function of another class:

    ldstr "Test String"

    ldftn instance string [mscorlib]System.String::ToLower()

    pop

    pop

    -Shawn

  3. Very,

    Very interesting interview by Anders Hejlsberg (the lead C#

    architect) with Bruce Eckel…