Merge pull request #406 from nspcc-dev/fix/fail-on-first-error
VM: do not pop too many items from stack JSON tests in neo-vm check stack state even in case of failure. There are 2 corrections need to be done: Pop items in EQUAL one-by-one. Do not pop anything from stack in OVER. I have also implemented operand restriction for SHL/SHR.
This commit is contained in:
commit
317dc6c5ed
2 changed files with 52 additions and 26 deletions
46
pkg/vm/vm.go
46
pkg/vm/vm.go
|
@ -22,6 +22,11 @@ var (
|
||||||
ModeMute Mode = 1 << 0
|
ModeMute Mode = 1 << 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxSHLArg = 256
|
||||||
|
minSHLArg = -256
|
||||||
|
)
|
||||||
|
|
||||||
// VM represents the virtual machine.
|
// VM represents the virtual machine.
|
||||||
type VM struct {
|
type VM struct {
|
||||||
state State
|
state State
|
||||||
|
@ -318,18 +323,22 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
||||||
v.estack.PushVal(ab)
|
v.estack.PushVal(ab)
|
||||||
case SUBSTR:
|
case SUBSTR:
|
||||||
l := int(v.estack.Pop().BigInt().Int64())
|
l := int(v.estack.Pop().BigInt().Int64())
|
||||||
o := int(v.estack.Pop().BigInt().Int64())
|
|
||||||
s := v.estack.Pop().Bytes()
|
|
||||||
if l < 0 {
|
if l < 0 {
|
||||||
panic("negative length")
|
panic("negative length")
|
||||||
}
|
}
|
||||||
|
o := int(v.estack.Pop().BigInt().Int64())
|
||||||
if o < 0 {
|
if o < 0 {
|
||||||
panic("negative index")
|
panic("negative index")
|
||||||
}
|
}
|
||||||
if l+o > len(s) {
|
s := v.estack.Pop().Bytes()
|
||||||
panic("out of bounds access")
|
if o > len(s) {
|
||||||
|
panic("invalid offset")
|
||||||
}
|
}
|
||||||
v.estack.PushVal(s[o : o+l])
|
last := l + o
|
||||||
|
if last > len(s) {
|
||||||
|
last = len(s)
|
||||||
|
}
|
||||||
|
v.estack.PushVal(s[o:last])
|
||||||
case LEFT:
|
case LEFT:
|
||||||
l := int(v.estack.Pop().BigInt().Int64())
|
l := int(v.estack.Pop().BigInt().Int64())
|
||||||
if l < 0 {
|
if l < 0 {
|
||||||
|
@ -404,15 +413,10 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case OVER:
|
case OVER:
|
||||||
b := v.estack.Pop()
|
a := v.estack.Peek(1)
|
||||||
if b == nil {
|
|
||||||
panic("no top-level element found")
|
|
||||||
}
|
|
||||||
a := v.estack.Peek(0)
|
|
||||||
if a == nil {
|
if a == nil {
|
||||||
panic("no second element found")
|
panic("no second element found")
|
||||||
}
|
}
|
||||||
v.estack.Push(b)
|
|
||||||
v.estack.Push(a)
|
v.estack.Push(a)
|
||||||
|
|
||||||
case PICK:
|
case PICK:
|
||||||
|
@ -509,21 +513,19 @@ func (v *VM) execute(ctx *Context, op Instruction) {
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Mod(a, b))
|
v.estack.PushVal(new(big.Int).Mod(a, b))
|
||||||
|
|
||||||
case SHL:
|
case SHL, SHR:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt().Int64()
|
||||||
if b.Int64() == 0 {
|
if b == 0 {
|
||||||
return
|
return
|
||||||
|
} else if b < minSHLArg || b > maxSHLArg {
|
||||||
|
panic(fmt.Sprintf("operand must be between %d and %d", minSHLArg, maxSHLArg))
|
||||||
}
|
}
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Lsh(a, uint(b.Int64())))
|
if op == SHL {
|
||||||
|
v.estack.PushVal(new(big.Int).Lsh(a, uint(b)))
|
||||||
case SHR:
|
} else {
|
||||||
b := v.estack.Pop().BigInt()
|
v.estack.PushVal(new(big.Int).Rsh(a, uint(b)))
|
||||||
if b.Int64() == 0 {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
a := v.estack.Pop().BigInt()
|
|
||||||
v.estack.PushVal(new(big.Int).Rsh(a, uint(b.Int64())))
|
|
||||||
|
|
||||||
case BOOLAND:
|
case BOOLAND:
|
||||||
b := v.estack.Pop().Bool()
|
b := v.estack.Pop().Bool()
|
||||||
|
|
|
@ -305,6 +305,15 @@ func TestSHRZero(t *testing.T) {
|
||||||
assert.Equal(t, makeStackItem([]byte{0, 1}), vm.estack.Pop().value)
|
assert.Equal(t, makeStackItem([]byte{0, 1}), vm.estack.Pop().value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSHRSmallValue(t *testing.T) {
|
||||||
|
prog := makeProgram(SHR)
|
||||||
|
vm := load(prog)
|
||||||
|
vm.estack.PushVal(5)
|
||||||
|
vm.estack.PushVal(-257)
|
||||||
|
vm.Run()
|
||||||
|
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||||
|
}
|
||||||
|
|
||||||
func TestSHLGood(t *testing.T) {
|
func TestSHLGood(t *testing.T) {
|
||||||
prog := makeProgram(SHL)
|
prog := makeProgram(SHL)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
|
@ -327,6 +336,15 @@ func TestSHLZero(t *testing.T) {
|
||||||
assert.Equal(t, makeStackItem([]byte{0, 1}), vm.estack.Pop().value)
|
assert.Equal(t, makeStackItem([]byte{0, 1}), vm.estack.Pop().value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSHLBigValue(t *testing.T) {
|
||||||
|
prog := makeProgram(SHL)
|
||||||
|
vm := load(prog)
|
||||||
|
vm.estack.PushVal(5)
|
||||||
|
vm.estack.PushVal(257)
|
||||||
|
vm.Run()
|
||||||
|
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||||
|
}
|
||||||
|
|
||||||
func TestLT(t *testing.T) {
|
func TestLT(t *testing.T) {
|
||||||
prog := makeProgram(LT)
|
prog := makeProgram(LT)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
|
@ -920,6 +938,8 @@ func TestOVERbadNoitem(t *testing.T) {
|
||||||
vm.estack.PushVal(1)
|
vm.estack.PushVal(1)
|
||||||
vm.Run()
|
vm.Run()
|
||||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||||
|
assert.Equal(t, 1, vm.estack.Len())
|
||||||
|
assert.Equal(t, makeStackItem(1), vm.estack.Pop().value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOVERbadNoitems(t *testing.T) {
|
func TestOVERbadNoitems(t *testing.T) {
|
||||||
|
@ -1138,20 +1158,22 @@ func TestSUBSTRBadOffset(t *testing.T) {
|
||||||
prog := makeProgram(SUBSTR)
|
prog := makeProgram(SUBSTR)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal([]byte("abcdef"))
|
vm.estack.PushVal([]byte("abcdef"))
|
||||||
vm.estack.PushVal(6)
|
vm.estack.PushVal(7)
|
||||||
vm.estack.PushVal(1)
|
vm.estack.PushVal(1)
|
||||||
vm.Run()
|
vm.Run()
|
||||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSUBSTRBadLen(t *testing.T) {
|
func TestSUBSTRBigLen(t *testing.T) {
|
||||||
prog := makeProgram(SUBSTR)
|
prog := makeProgram(SUBSTR)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal([]byte("abcdef"))
|
vm.estack.PushVal([]byte("abcdef"))
|
||||||
vm.estack.PushVal(1)
|
vm.estack.PushVal(1)
|
||||||
vm.estack.PushVal(6)
|
vm.estack.PushVal(6)
|
||||||
vm.Run()
|
vm.Run()
|
||||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||||
|
assert.Equal(t, 1, vm.estack.Len())
|
||||||
|
assert.Equal(t, []byte("bcdef"), vm.estack.Pop().Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSUBSTRBad387(t *testing.T) {
|
func TestSUBSTRBad387(t *testing.T) {
|
||||||
|
@ -1163,7 +1185,9 @@ func TestSUBSTRBad387(t *testing.T) {
|
||||||
vm.estack.PushVal(1)
|
vm.estack.PushVal(1)
|
||||||
vm.estack.PushVal(6)
|
vm.estack.PushVal(6)
|
||||||
vm.Run()
|
vm.Run()
|
||||||
assert.Equal(t, true, vm.state.HasFlag(faultState))
|
assert.Equal(t, false, vm.state.HasFlag(faultState))
|
||||||
|
assert.Equal(t, 1, vm.estack.Len())
|
||||||
|
assert.Equal(t, []byte("bcdef"), vm.estack.Pop().Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSUBSTRBadNegativeOffset(t *testing.T) {
|
func TestSUBSTRBadNegativeOffset(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue