Skip to content

Neo VM should clear evaluation stack when exception is thrown #3691

@Hecate2

Description

@Hecate2

Describe the bug
Please see below, or a new test case at https://github.com/neo-project/neo-devpack-dotnet/pull/1283/files#diff-544e2dcb82be726cf5037a446b68a0111e7851c29aafea4173b6565f7cef123e

To Reproduce
Compile the following contracts with the latest master branch neo-project/neo-devpack-dotnet@db9248a , or the codes at neo-project/neo-devpack-dotnet#1283 .

namespace Neo.Compiler.CSharp.TestContracts
{
    public class Contract_Returns : SmartContract.Framework.SmartContract
    {
        private int MinimalTryReturnInternal()
        {
            try { return 1; }
            finally { throw new System.Exception(); }  // should execute finally after return
        }

        public void MinimalTryReturn()
        {
            try { MinimalTryReturnInternal(); }
            catch { return; }
        }
    }
}
# Method Start Neo.Compiler.CSharp.TestContracts.Contract_Returns.MinimalTryReturnInternal()
00 TRY 00-06 # no catch block, finally pos: 6 (offset: 6)
# Code test-compiler.cs line 21: "1"
03 PUSH1
# Code test-compiler.cs line 21: "return 1;"
04 ENDTRY 00 # pos: 4 (offset: 0)
# Code test-compiler.cs line 22: "throw new System.Exception();"
06 PUSHDATA1 65-78-63-65-70-74-69-6F-6E # as text: "exception"
# Method End Neo.Compiler.CSharp.TestContracts.Contract_Returns.MinimalTryReturnInternal()
# Code test-compiler.cs line 22: "throw new System.Exception();"
# Code test-compiler.cs line 23: "}"
17 THROW
# Method Start Neo.Compiler.CSharp.TestContracts.Contract_Returns.MinimalTryReturn()
18 INITSLOT 01-00 # 1 local variables, 0 arguments
21 TRY 05-00 # catch pos: 26 (offset: 5), no finally block
# Code test-compiler.cs line 27: "MinimalTryReturnInternal()"
# Code test-compiler.cs line 27: "MinimalTryReturnInternal()"
24 CALL E8 # pos: 0 (offset: -24)
# Code test-compiler.cs line 28: "catch"
26 STLOC0
# Code test-compiler.cs line 28: "return;"
27 ENDTRY 02 # pos: 29 (offset: 2)
# Method End Neo.Compiler.CSharp.TestContracts.Contract_Returns.MinimalTryReturn()
# Code test-compiler.cs line 29: "}"
29 RET

Call minimalTryReturn to get exception Return value count mismatch: expected 0, but got 1 items on the evaluation stack. This is because 1 is pushed into the evaluation stack when MinimalTryReturnInternal tries to return 1, and the external context has no way to DROP the returned 1, because there is an exception to be catched.

And it seems impossible for the compiler to deal with the problem. During the execution of an expression, there is always possibility of exception at any instruction, and the exception can always be caught by the user to generate orphaned items in the evaluation stack.

Expected behavior
No exception

Possible Solutions

  • Exception clears the evaluation stack
  • (Optional) ENDFINALLY clears the evaluation stack
  • TRY may have a higher GAS price, if necessary. Current price for TRY is 4, and CLEAR has 16.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions