Skip to content
This repository has been archived by the owner on Nov 22, 2023. It is now read-only.

Mode to disable backward jumps on NeoVM 3 #174

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/neo-vm/Debugger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ public void AddBreakPoint(Script script, uint position)
hashset.Add(position);
}

public VMState Execute()
public VMState Execute(bool isLimited = false)
{
engine.State &= ~VMState.BREAK;
while (!engine.State.HasFlag(VMState.HALT) && !engine.State.HasFlag(VMState.FAULT) && !engine.State.HasFlag(VMState.BREAK))
ExecuteAndCheckBreakPoints();
ExecuteAndCheckBreakPoints(isLimited);
return engine.State;
}

private void ExecuteAndCheckBreakPoints()
private void ExecuteAndCheckBreakPoints(bool isLimited)
{
engine.ExecuteNext();
engine.ExecuteNext(isLimited);
if (engine.State == VMState.NONE && engine.InvocationStack.Count > 0 && break_points.Count > 0)
{
if (break_points.TryGetValue(engine.CurrentContext.Script, out HashSet<uint> hashset) && hashset.Contains((uint)engine.CurrentContext.InstructionPointer))
Expand All @@ -48,36 +48,36 @@ public bool RemoveBreakPoint(Script script, uint position)
return true;
}

public VMState StepInto()
public VMState StepInto(bool isLimited = false)
{
if (engine.State.HasFlag(VMState.HALT) || engine.State.HasFlag(VMState.FAULT))
return engine.State;
engine.ExecuteNext();
engine.ExecuteNext(isLimited);
if (engine.State == VMState.NONE)
engine.State = VMState.BREAK;
return engine.State;
}

public VMState StepOut()
public VMState StepOut(bool isLimited)
{
engine.State &= ~VMState.BREAK;
int c = engine.InvocationStack.Count;
while (!engine.State.HasFlag(VMState.HALT) && !engine.State.HasFlag(VMState.FAULT) && !engine.State.HasFlag(VMState.BREAK) && engine.InvocationStack.Count >= c)
ExecuteAndCheckBreakPoints();
ExecuteAndCheckBreakPoints(isLimited);
if (engine.State == VMState.NONE)
engine.State = VMState.BREAK;
return engine.State;
}

public VMState StepOver()
public VMState StepOver(bool isLimited = false)
{
if (engine.State.HasFlag(VMState.HALT) || engine.State.HasFlag(VMState.FAULT))
return engine.State;
engine.State &= ~VMState.BREAK;
int c = engine.InvocationStack.Count;
do
{
ExecuteAndCheckBreakPoints();
ExecuteAndCheckBreakPoints(isLimited);
}
while (!engine.State.HasFlag(VMState.HALT) && !engine.State.HasFlag(VMState.FAULT) && !engine.State.HasFlag(VMState.BREAK) && engine.InvocationStack.Count > c);
if (engine.State == VMState.NONE)
Expand Down
37 changes: 31 additions & 6 deletions src/neo-vm/ExecutionEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,15 @@ public virtual void Dispose()
InvocationStack.Clear();
}

public VMState Execute()
public VMState Execute(bool isLimited = false)
{
State &= ~VMState.BREAK;
while (!State.HasFlag(VMState.HALT) && !State.HasFlag(VMState.FAULT))
ExecuteNext();
ExecuteNext(isLimited);
return State;
}

internal protected void ExecuteNext()
internal protected void ExecuteNext(bool isLimited = false)
{
if (InvocationStack.Count == 0)
{
Expand All @@ -194,7 +194,7 @@ internal protected void ExecuteNext()
try
{
Instruction instruction = CurrentContext.CurrentInstruction;
if (!PreExecuteInstruction() || !ExecuteInstruction() || !PostExecuteInstruction(instruction))
if (!PreExecuteInstruction(isLimited) || !ExecuteInstruction() || !PostExecuteInstruction(instruction))
State = VMState.FAULT;
}
catch
Expand Down Expand Up @@ -1088,9 +1088,34 @@ public ExecutionContext LoadScript(byte[] script, int rvcount = -1)
}

protected virtual bool OnSysCall(uint method) => false;

protected virtual bool PostExecuteInstruction(Instruction instruction) => true;

protected virtual bool PreExecuteInstruction() => true;
protected virtual bool PreExecuteInstruction(bool isLimited = false)
{
ExecutionContext context = CurrentContext;
Instruction instruction = context.CurrentInstruction;
switch(instruction.OpCode)
{
case OpCode.JMP:
case OpCode.JMPIF:
case OpCode.JMPIFNOT:
case OpCode.CALL:
{
if(isLimited)
{
// no backwards jump in limited mode
if(instruction.TokenI16 <= 0)
{
return false;
}
}
break;
}
default:
return true;
}
return true;
}
}
}
35 changes: 35 additions & 0 deletions tests/neo-vm.Tests/Tests/OpCodes/Jumps/JMP.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"category": "Jumps",
"name": "JMP",
"tests":
[
{
"name": "while(true)",
"script": "0x620000",
"isLimited": true,
"steps":
[
{
"actions":
[
"Execute"
],
"result":
{
"state": "FAULT",
"invocationStack":
[
{
"instructionPointer": 0,
"nextInstruction": "JMP",
"evaluationStack":
[
]
}
]
}
}
]
}
]
}
35 changes: 35 additions & 0 deletions tests/neo-vm.Tests/Tests/OpCodes/JumpsUnlimited/JMP.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"category": "JumpsUnlimited",
"name": "JMP",
"tests":
[
{
"name": "while(true) timeout 3000ms",
"script": "0x620000",
"isLimited": false,
"steps":
[
{
"actions":
[
"Execute"
],
"result":
{
"state": "FAULT",
"invocationStack":
[
{
"instructionPointer": 3,
"nextInstruction": "RET",
"evaluationStack":
[
]
}
]
}
}
]
}
]
}
3 changes: 3 additions & 0 deletions tests/neo-vm.Tests/Types/VMUTEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ public class VMUTEntry
[JsonProperty]
public string Name { get; set; }

[JsonProperty]
public bool IsLimited { get; set; }

[JsonProperty]
public VMUTStep[] Steps { get; set; }
}
Expand Down
20 changes: 20 additions & 0 deletions tests/neo-vm.Tests/UtVMJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Test.Extensions;
using Neo.Test.Types;
using System.Threading.Tasks;
using System;
using System.Diagnostics;

namespace Neo.Test
{
Expand All @@ -15,6 +18,23 @@ public class UtVMJson : VMJsonTestBase
[TestMethod]
public void TestOpCodesArrays() => TestJson("./Tests/OpCodes/Arrays");

[TestMethod]
public void TestOpCodesJumps() => TestJson("./Tests/OpCodes/Jumps");

[TestMethod]
[Timeout(3500)]
public void TestOpCodesJumpsUnlimited()
{
//Task.Factory.StartNew(() => TestJson("./Tests/OpCodes/JumpsUnlimited")).Wait(TimeSpan.FromSeconds(3));
var stopwatch = new Stopwatch();
stopwatch.Start();
var task1 = Task.Factory.StartNew(() => TestJson("./Tests/OpCodes/JumpsUnlimited"));
task1.Wait(TimeSpan.FromSeconds(3));
stopwatch.Stop();
// should fail on timer basis
Assert.IsTrue(stopwatch.ElapsedMilliseconds >= 3000);
}

[TestMethod]
public void TestOpCodesStack() => TestJson("./Tests/OpCodes/Stack");

Expand Down
9 changes: 5 additions & 4 deletions tests/neo-vm.Tests/VMJsonTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Neo.VM;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Linq;

namespace Neo.Test
{
Expand Down Expand Up @@ -35,10 +36,10 @@ public void ExecuteTest(VMUT ut)
{
switch (run)
{
case VMUTActionType.Execute: debugger.Execute(); break;
case VMUTActionType.StepInto: debugger.StepInto(); break;
case VMUTActionType.StepOut: debugger.StepOut(); break;
case VMUTActionType.StepOver: debugger.StepOver(); break;
case VMUTActionType.Execute: debugger.Execute(test.IsLimited); break;
case VMUTActionType.StepInto: debugger.StepInto(test.IsLimited); break;
case VMUTActionType.StepOut: debugger.StepOut(test.IsLimited); break;
case VMUTActionType.StepOver: debugger.StepOver(test.IsLimited); break;
}
}

Expand Down