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

Add StrictMode #392

Merged
merged 6 commits into from
Jan 28, 2021
Merged
Show file tree
Hide file tree
Changes from 5 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
10 changes: 10 additions & 0 deletions src/neo-vm/BadScriptException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace Neo.VM
{
public class BadScriptException : Exception
{
public BadScriptException() { }
public BadScriptException(string message) : base(message) { }
}
}
6 changes: 3 additions & 3 deletions src/neo-vm/Instruction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,12 @@ static Instruction()

private Instruction(OpCode opcode)
{
OpCode = opcode;
this.OpCode = opcode;
if (!Enum.IsDefined(opcode)) throw new BadScriptException();
}

internal Instruction(byte[] script, int ip)
internal Instruction(byte[] script, int ip) : this((OpCode)script[ip++])
{
OpCode = (OpCode)script[ip++];
int operandSizePrefix = OperandSizePrefixTable[(int)OpCode];
int operandSize = 0;
switch (operandSizePrefix)
Expand Down
74 changes: 72 additions & 2 deletions src/neo-vm/Script.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Neo.VM.Types;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
Expand All @@ -8,6 +10,7 @@ namespace Neo.VM
public class Script
{
private readonly byte[] _value;
private readonly bool strictMode;
private readonly Dictionary<int, Instruction> _instructions = new Dictionary<int, Instruction>();

/// <summary>
Expand Down Expand Up @@ -40,16 +43,83 @@ public OpCode this[int index]
/// Constructor
/// </summary>
/// <param name="script">Script</param>
public Script(byte[] script)
public Script(byte[] script) : this(script, false)
{
_value = script;
}

public Script(byte[] script, bool strictMode)
{
this._value = script;
if (strictMode)
{
for (int ip = 0; ip < script.Length; ip += GetInstruction(ip).Size)
;
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
foreach (var (ip, instruction) in _instructions)
{
switch (instruction.OpCode)
{
case OpCode.JMP:
case OpCode.JMPIF:
case OpCode.JMPIFNOT:
case OpCode.JMPEQ:
case OpCode.JMPNE:
case OpCode.JMPGT:
case OpCode.JMPGE:
case OpCode.JMPLT:
case OpCode.JMPLE:
case OpCode.CALL:
case OpCode.ENDTRY:
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI8)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
break;
case OpCode.PUSHA:
case OpCode.JMP_L:
case OpCode.JMPIF_L:
case OpCode.JMPIFNOT_L:
case OpCode.JMPEQ_L:
case OpCode.JMPNE_L:
case OpCode.JMPGT_L:
case OpCode.JMPGE_L:
case OpCode.JMPLT_L:
case OpCode.JMPLE_L:
case OpCode.CALL_L:
case OpCode.ENDTRY_L:
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI32)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
break;
case OpCode.TRY:
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI8)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI8_1)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
break;
case OpCode.TRY_L:
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI32)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
if (!_instructions.ContainsKey(checked(ip + instruction.TokenI32_1)))
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
break;
case OpCode.NEWARRAY_T:
case OpCode.ISTYPE:
case OpCode.CONVERT:
StackItemType type = (StackItemType)instruction.TokenU8;
if (!Enum.IsDefined(typeof(StackItemType), type))
throw new BadScriptException();
if (instruction.OpCode != OpCode.NEWARRAY_T && type == StackItemType.Any)
throw new BadScriptException($"ip: {ip}, opcode: {instruction.OpCode}");
break;
}
}
}
this.strictMode = strictMode;
}

public Instruction GetInstruction(int ip)
{
if (ip >= Length) return Instruction.RET;
if (!_instructions.TryGetValue(ip, out Instruction instruction))
{
if (strictMode) throw new ArgumentException($"ip not found with strict mode", nameof(ip));
instruction = new Instruction(_value, ip);
_instructions.Add(ip, instruction);
}
Expand Down
10 changes: 10 additions & 0 deletions tests/neo-vm.Tests/UtScript.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ public void Conversion()
CollectionAssert.AreEqual(rawScript, scriptConversion);
}

[TestMethod]
public void StrictMode()
{
var rawScript = new byte[] { (byte)OpCode.PUSH0, 0xFF };
Assert.ThrowsException<BadScriptException>(() => new Script(rawScript, true));

var script = new Script(rawScript, false);
Assert.AreEqual(2, script.Length);
}

[TestMethod]
public void Parse()
{
Expand Down