Merge pull request #2209 from nspcc-dev/vm-exceptions
Catch out-of-bounds exceptions in VM
This commit is contained in:
commit
e2c4b6d393
2 changed files with 74 additions and 8 deletions
24
pkg/vm/vm.go
24
pkg/vm/vm.go
|
@ -1087,27 +1087,33 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
validateMapKey(key)
|
validateMapKey(key)
|
||||||
|
|
||||||
obj := v.estack.Pop()
|
obj := v.estack.Pop()
|
||||||
index := int(key.BigInt().Int64())
|
|
||||||
|
|
||||||
switch t := obj.value.(type) {
|
switch t := obj.value.(type) {
|
||||||
// Struct and Array items have their underlying value as []Item.
|
// Struct and Array items have their underlying value as []Item.
|
||||||
case *stackitem.Array, *stackitem.Struct:
|
case *stackitem.Array, *stackitem.Struct:
|
||||||
|
index := toInt(key.BigInt())
|
||||||
arr := t.Value().([]stackitem.Item)
|
arr := t.Value().([]stackitem.Item)
|
||||||
if index < 0 || index >= len(arr) {
|
if index < 0 || index >= len(arr) {
|
||||||
panic("PICKITEM: invalid index")
|
msg := fmt.Sprintf("The value %d is out of range.", index)
|
||||||
|
v.throw(stackitem.NewByteArray([]byte(msg)))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
item := arr[index].Dup()
|
item := arr[index].Dup()
|
||||||
v.estack.PushItem(item)
|
v.estack.PushItem(item)
|
||||||
case *stackitem.Map:
|
case *stackitem.Map:
|
||||||
index := t.Index(key.Item())
|
index := t.Index(key.Item())
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
panic("invalid key")
|
v.throw(stackitem.NewByteArray([]byte("Key not found in Map")))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
v.estack.PushItem(t.Value().([]stackitem.MapElement)[index].Value.Dup())
|
v.estack.PushItem(t.Value().([]stackitem.MapElement)[index].Value.Dup())
|
||||||
default:
|
default:
|
||||||
|
index := toInt(key.BigInt())
|
||||||
arr := obj.Bytes()
|
arr := obj.Bytes()
|
||||||
if index < 0 || index >= len(arr) {
|
if index < 0 || index >= len(arr) {
|
||||||
panic("PICKITEM: invalid index")
|
msg := fmt.Sprintf("The value %d is out of range.", index)
|
||||||
|
v.throw(stackitem.NewByteArray([]byte(msg)))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
item := arr[index]
|
item := arr[index]
|
||||||
v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(item))))
|
v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(item))))
|
||||||
|
@ -1124,9 +1130,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
// Struct and Array items have their underlying value as []Item.
|
// Struct and Array items have their underlying value as []Item.
|
||||||
case *stackitem.Array, *stackitem.Struct:
|
case *stackitem.Array, *stackitem.Struct:
|
||||||
arr := t.Value().([]stackitem.Item)
|
arr := t.Value().([]stackitem.Item)
|
||||||
index := int(key.BigInt().Int64())
|
index := toInt(key.BigInt())
|
||||||
if index < 0 || index >= len(arr) {
|
if index < 0 || index >= len(arr) {
|
||||||
panic("SETITEM: invalid index")
|
msg := fmt.Sprintf("The value %d is out of range.", index)
|
||||||
|
v.throw(stackitem.NewByteArray([]byte(msg)))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
v.refs.Remove(arr[index])
|
v.refs.Remove(arr[index])
|
||||||
arr[index] = item
|
arr[index] = item
|
||||||
|
@ -1141,7 +1149,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
case *stackitem.Buffer:
|
case *stackitem.Buffer:
|
||||||
index := toInt(key.BigInt())
|
index := toInt(key.BigInt())
|
||||||
if index < 0 || index >= t.Len() {
|
if index < 0 || index >= t.Len() {
|
||||||
panic("invalid index")
|
msg := fmt.Sprintf("The value %d is out of range.", index)
|
||||||
|
v.throw(stackitem.NewByteArray([]byte(msg)))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
bi, err := item.TryInteger()
|
bi, err := item.TryInteger()
|
||||||
b := toInt(bi)
|
b := toInt(bi)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -1158,6 +1159,26 @@ func TestPICKITEM(t *testing.T) {
|
||||||
t.Run("Array", getTestFuncForVM(prog, 2, []stackitem.Item{stackitem.Make(1), stackitem.Make(2)}, 1))
|
t.Run("Array", getTestFuncForVM(prog, 2, []stackitem.Item{stackitem.Make(1), stackitem.Make(2)}, 1))
|
||||||
t.Run("ByteArray", getTestFuncForVM(prog, 2, []byte{1, 2}, 1))
|
t.Run("ByteArray", getTestFuncForVM(prog, 2, []byte{1, 2}, 1))
|
||||||
t.Run("Buffer", getTestFuncForVM(prog, 2, stackitem.NewBuffer([]byte{1, 2}), 1))
|
t.Run("Buffer", getTestFuncForVM(prog, 2, stackitem.NewBuffer([]byte{1, 2}), 1))
|
||||||
|
t.Run("Exceptions", func(t *testing.T) {
|
||||||
|
tryProg := getTRYProgram(
|
||||||
|
[]byte{byte(opcode.PICKITEM), byte(opcode.RET)},
|
||||||
|
[]byte{byte(opcode.RET)}, nil)
|
||||||
|
items := []stackitem.Item{
|
||||||
|
stackitem.NewArray([]stackitem.Item{}),
|
||||||
|
stackitem.NewBuffer([]byte{}),
|
||||||
|
stackitem.NewByteArray([]byte{}),
|
||||||
|
}
|
||||||
|
for _, item := range items {
|
||||||
|
t.Run(item.String()+", negative", getTestFuncForVM(tryProg,
|
||||||
|
fmt.Sprintf("The value %d is out of range.", math.MinInt32),
|
||||||
|
item, math.MinInt32))
|
||||||
|
t.Run(item.String()+", very big index",
|
||||||
|
getTestFuncForVM(tryProg, nil, item, int64(math.MaxInt32)+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
m := stackitem.NewMap()
|
||||||
|
t.Run("Map, missing key", getTestFuncForVM(tryProg, "Key not found in Map", m, 1))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPICKITEMDupArray(t *testing.T) {
|
func TestPICKITEMDupArray(t *testing.T) {
|
||||||
|
@ -1196,8 +1217,33 @@ func TestPICKITEMMap(t *testing.T) {
|
||||||
func TestSETITEMBuffer(t *testing.T) {
|
func TestSETITEMBuffer(t *testing.T) {
|
||||||
prog := makeProgram(opcode.DUP, opcode.REVERSE4, opcode.SETITEM)
|
prog := makeProgram(opcode.DUP, opcode.REVERSE4, opcode.SETITEM)
|
||||||
t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte{0, 42, 2}), 42, 1, stackitem.NewBuffer([]byte{0, 1, 2})))
|
t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte{0, 42, 2}), 42, 1, stackitem.NewBuffer([]byte{0, 1, 2})))
|
||||||
t.Run("BadIndex", getTestFuncForVM(prog, nil, 42, -1, stackitem.NewBuffer([]byte{0, 1, 2})))
|
|
||||||
t.Run("BadValue", getTestFuncForVM(prog, nil, 256, 1, stackitem.NewBuffer([]byte{0, 1, 2})))
|
t.Run("BadValue", getTestFuncForVM(prog, nil, 256, 1, stackitem.NewBuffer([]byte{0, 1, 2})))
|
||||||
|
t.Run("Exceptions", func(t *testing.T) {
|
||||||
|
tryProg := getTRYProgram(
|
||||||
|
[]byte{byte(opcode.SETITEM), byte(opcode.PUSH12), byte(opcode.RET)},
|
||||||
|
[]byte{byte(opcode.RET)}, nil)
|
||||||
|
t.Run("negative index", getTestFuncForVM(tryProg,
|
||||||
|
fmt.Sprintf("The value %d is out of range.", math.MinInt32),
|
||||||
|
stackitem.NewBuffer([]byte{0, 1, 2}), math.MinInt32, 0))
|
||||||
|
t.Run("very big index", getTestFuncForVM(tryProg,
|
||||||
|
nil, stackitem.NewBuffer([]byte{0, 1, 2}), int64(math.MaxInt32)+1, 0))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSETITEMArray(t *testing.T) {
|
||||||
|
tryProg := getTRYProgram(
|
||||||
|
[]byte{byte(opcode.SETITEM), byte(opcode.RET)},
|
||||||
|
[]byte{byte(opcode.RET)}, nil)
|
||||||
|
t.Run("Good", func(t *testing.T) {
|
||||||
|
arr := stackitem.NewArray([]stackitem.Item{stackitem.Make(12), stackitem.Make(2)})
|
||||||
|
expected := stackitem.NewArray([]stackitem.Item{stackitem.Make(12), stackitem.Make(42)})
|
||||||
|
runWithArgs(t, tryProg, expected, arr, arr, 1, 42)
|
||||||
|
})
|
||||||
|
t.Run("negative index", getTestFuncForVM(tryProg,
|
||||||
|
fmt.Sprintf("The value %d is out of range.", math.MinInt32),
|
||||||
|
[]stackitem.Item{}, math.MinInt32, 42))
|
||||||
|
t.Run("very big index", getTestFuncForVM(tryProg,
|
||||||
|
nil, []stackitem.Item{}, int64(math.MaxInt32)+1, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSETITEMMap(t *testing.T) {
|
func TestSETITEMMap(t *testing.T) {
|
||||||
|
@ -1205,6 +1251,16 @@ func TestSETITEMMap(t *testing.T) {
|
||||||
m := stackitem.NewMap()
|
m := stackitem.NewMap()
|
||||||
m.Add(stackitem.Make(5), stackitem.Make(3))
|
m.Add(stackitem.Make(5), stackitem.Make(3))
|
||||||
runWithArgs(t, prog, []byte{0, 1}, m, 5, m, 5, []byte{0, 1})
|
runWithArgs(t, prog, []byte{0, 1}, m, 5, m, 5, []byte{0, 1})
|
||||||
|
|
||||||
|
t.Run("big key", func(t *testing.T) {
|
||||||
|
m := stackitem.NewMap()
|
||||||
|
key := make([]byte, stackitem.MaxKeySize)
|
||||||
|
for i := range key {
|
||||||
|
key[i] = 0x0F
|
||||||
|
}
|
||||||
|
m.Add(stackitem.NewByteArray(key), stackitem.Make(3))
|
||||||
|
runWithArgs(t, prog, "value", m, key, m, key, "value")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSETITEMBigMapBad(t *testing.T) {
|
func TestSETITEMBigMapBad(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue