Hi

I found that Cecil makes wrong stack depth calculation for the
following IL code:

.assembly Hello {}
.method public static void Main() cil managed
{
      .entrypoint
      ldstr "Hello, world!"
      br L1
      nop
L1:
      ldstr "Hello, Cecil!"
      br L2
      nop
L2:
      ldstr "More text"
      call void [mscorlib]System.Console::WriteLine(string)
      call void [mscorlib]System.Console::WriteLine(string)
      call void [mscorlib]System.Console::WriteLine(string)
      ret
}

Cecil calculates this as 2. However it's obvious that stack size of
the above code is 3 because it has 3 pushes and 3 pops.

Code above is not verifiable CIL code, and ECMA CIL specification is
very vague regarding such a code. However, the sample above works
successfully with Microsoft .NET framework. Also such code is produced
by some obfuscators and such transformation is called 'control flow
obfuscation'.

It's clear that Microsoft .NET implementation do not clear the stack
on branching instructions. But Mono.Cecil assumes that stack is always
cleared on branching. That's why it may calculate lower max stack size
than it is in reality. It often leads to an exception which says
"Common Language Runtime detected an invalid program" when output
assembly is run.

I've made a change to Cecil that turns off the clearing of the stack
on branching instructions. Then I've tested max size calculations on
verifiable CIL code which is the most common one and produced by
all .NET compilers. Large open-source assemblies have been used for
the test. No changes to max stack values were detected comparing to
original Cecil algorithm.

Then I've created several assemblies with unverifiable CIL code and
tried to apply round-trip procedure with Cecil. Some max stack values
were greater comparing to original Cecil. And the main result - the
assemblies with unverifiable CIL code produced by Cecil have started
to work normally under Microsoft .NET.

Here is the patch:

Index: CodeWriter.cs
===================================================================
--- CodeWriter.cs       (revision 130916)
+++ CodeWriter.cs       (working copy)
@@ -478,7 +478,6 @@
                                }

                                switch (instr.OpCode.FlowControl) {
-                               case FlowControl.Branch:
                                case FlowControl.Throw:
                                case FlowControl.Return:
                                        // next statement is not reachable from 
this statement, so reset
the stack depth to 0

Regards
Oleksiy
--~--~---------~--~----~------------~-------~--~----~
--
mono-cecil
-~----------~----~----~----~------~----~------~--~---

Reply via email to