vm: fix integer conversions

As can be seen in https://dotnetfiddle.net/s7eg21 (int) conversions
result in an exception in C# code.

Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgeniy Stratonikov 2022-03-09 17:34:36 +03:00
parent 6ece74a7c7
commit 39866b8512
2 changed files with 39 additions and 15 deletions

View file

@ -723,11 +723,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.estack.PushItem(stackitem.NewBuffer(ab)) v.estack.PushItem(stackitem.NewBuffer(ab))
case opcode.SUBSTR: case opcode.SUBSTR:
l := int(v.estack.Pop().BigInt().Int64()) l := toInt(v.estack.Pop().BigInt())
if l < 0 { if l < 0 {
panic("negative length") panic("negative length")
} }
o := int(v.estack.Pop().BigInt().Int64()) o := toInt(v.estack.Pop().BigInt())
if o < 0 { if o < 0 {
panic("negative index") panic("negative index")
} }
@ -741,7 +741,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.estack.PushItem(stackitem.NewBuffer(res)) v.estack.PushItem(stackitem.NewBuffer(res))
case opcode.LEFT: case opcode.LEFT:
l := int(v.estack.Pop().BigInt().Int64()) l := toInt(v.estack.Pop().BigInt())
if l < 0 { if l < 0 {
panic("negative length") panic("negative length")
} }
@ -754,7 +754,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.estack.PushItem(stackitem.NewBuffer(res)) v.estack.PushItem(stackitem.NewBuffer(res))
case opcode.RIGHT: case opcode.RIGHT:
l := int(v.estack.Pop().BigInt().Int64()) l := toInt(v.estack.Pop().BigInt())
if l < 0 { if l < 0 {
panic("negative length") panic("negative length")
} }
@ -779,7 +779,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
_ = v.estack.RemoveAt(1) _ = v.estack.RemoveAt(1)
case opcode.XDROP: case opcode.XDROP:
n := int(v.estack.Pop().BigInt().Int64()) n := toInt(v.estack.Pop().BigInt())
if n < 0 { if n < 0 {
panic("invalid length") panic("invalid length")
} }
@ -802,7 +802,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.estack.Push(a) v.estack.Push(a)
case opcode.PICK: case opcode.PICK:
n := int(v.estack.Pop().BigInt().Int64()) n := toInt(v.estack.Pop().BigInt())
if n < 0 { if n < 0 {
panic("negative stack item returned") panic("negative stack item returned")
} }
@ -832,7 +832,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
case opcode.ROLL: case opcode.ROLL:
n := int(v.estack.Pop().BigInt().Int64()) n := toInt(v.estack.Pop().BigInt())
err := v.estack.Roll(n) err := v.estack.Roll(n)
if err != nil { if err != nil {
panic(err.Error()) panic(err.Error())
@ -844,7 +844,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.REVERSE4: case opcode.REVERSE4:
n = 4 n = 4
case opcode.REVERSEN: case opcode.REVERSEN:
n = int(v.estack.Pop().BigInt().Int64()) n = toInt(v.estack.Pop().BigInt())
} }
if err := v.estack.ReverseTop(n); err != nil { if err := v.estack.ReverseTop(n); err != nil {
panic(err.Error()) panic(err.Error())
@ -952,7 +952,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Sqrt(a))) v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Sqrt(a)))
case opcode.SHL, opcode.SHR: case opcode.SHL, opcode.SHR:
b := v.estack.Pop().BigInt().Int64() b := toInt(v.estack.Pop().BigInt())
if b == 0 { if b == 0 {
return return
} else if b < 0 || b > maxSHLArg { } else if b < 0 || b > maxSHLArg {
@ -1252,7 +1252,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
switch t := elem.value.(type) { switch t := elem.value.(type) {
case *stackitem.Array: case *stackitem.Array:
a := t.Value().([]stackitem.Item) a := t.Value().([]stackitem.Item)
k := int(key.BigInt().Int64()) k := toInt(key.BigInt())
if k < 0 || k >= len(a) { if k < 0 || k >= len(a) {
panic("REMOVE: invalid index") panic("REMOVE: invalid index")
} }
@ -1260,7 +1260,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
t.Remove(k) t.Remove(k)
case *stackitem.Struct: case *stackitem.Struct:
a := t.Value().([]stackitem.Item) a := t.Value().([]stackitem.Item)
k := int(key.BigInt().Int64()) k := toInt(key.BigInt())
if k < 0 || k >= len(a) { if k < 0 || k >= len(a) {
panic("REMOVE: invalid index") panic("REMOVE: invalid index")
} }
@ -1454,19 +1454,19 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
var res bool var res bool
switch t := c.value.(type) { switch t := c.value.(type) {
case *stackitem.Array, *stackitem.Struct: case *stackitem.Array, *stackitem.Struct:
index := key.BigInt().Int64() index := toInt(key.BigInt())
if index < 0 { if index < 0 {
panic("negative index") panic("negative index")
} }
res = index < int64(len(c.Array())) res = index < len(c.Array())
case *stackitem.Map: case *stackitem.Map:
res = t.Has(key.Item()) res = t.Has(key.Item())
case *stackitem.Buffer: case *stackitem.Buffer:
index := key.BigInt().Int64() index := toInt(key.BigInt())
if index < 0 { if index < 0 {
panic("negative index") panic("negative index")
} }
res = index < int64(t.Len()) res = index < t.Len()
default: default:
panic("wrong collection type") panic("wrong collection type")
} }

View file

@ -737,6 +737,7 @@ func TestSHR(t *testing.T) {
t.Run("Good", getTestFuncForVM(prog, 1, 4, 2)) t.Run("Good", getTestFuncForVM(prog, 1, 4, 2))
t.Run("Zero", getTestFuncForVM(prog, []byte{0, 1}, []byte{0, 1}, 0)) t.Run("Zero", getTestFuncForVM(prog, []byte{0, 1}, []byte{0, 1}, 0))
t.Run("Negative", getTestFuncForVM(prog, nil, 5, -1)) t.Run("Negative", getTestFuncForVM(prog, nil, 5, -1))
t.Run("very big", getTestFuncForVM(prog, nil, 5, maxu64Plus(1)))
} }
func TestSHL(t *testing.T) { func TestSHL(t *testing.T) {
@ -745,6 +746,7 @@ func TestSHL(t *testing.T) {
t.Run("Zero", getTestFuncForVM(prog, []byte{0, 1}, []byte{0, 1}, 0)) t.Run("Zero", getTestFuncForVM(prog, []byte{0, 1}, []byte{0, 1}, 0))
t.Run("BigShift", getTestFuncForVM(prog, nil, 5, maxSHLArg+1)) t.Run("BigShift", getTestFuncForVM(prog, nil, 5, maxSHLArg+1))
t.Run("BigResult", getTestFuncForVM(prog, nil, getBigInt(stackitem.MaxBigIntegerSizeBits/2, 0), stackitem.MaxBigIntegerSizeBits/2)) t.Run("BigResult", getTestFuncForVM(prog, nil, getBigInt(stackitem.MaxBigIntegerSizeBits/2, 0), stackitem.MaxBigIntegerSizeBits/2))
t.Run("very big shift", getTestFuncForVM(prog, nil, 5, maxu64Plus(1)))
} }
func TestArithNullArg(t *testing.T) { func TestArithNullArg(t *testing.T) {
@ -1418,15 +1420,18 @@ func TestHASKEY(t *testing.T) {
arr := makeArrayOfType(5, stackitem.BooleanT) arr := makeArrayOfType(5, stackitem.BooleanT)
t.Run("Array", func(t *testing.T) { t.Run("Array", func(t *testing.T) {
t.Run("True", getTestFuncForVM(prog, true, stackitem.NewArray(arr), 4)) t.Run("True", getTestFuncForVM(prog, true, stackitem.NewArray(arr), 4))
t.Run("too big", getTestFuncForVM(prog, nil, stackitem.NewArray(arr), maxu64Plus(4)))
t.Run("False", getTestFuncForVM(prog, false, stackitem.NewArray(arr), 5)) t.Run("False", getTestFuncForVM(prog, false, stackitem.NewArray(arr), 5))
}) })
t.Run("Struct", func(t *testing.T) { t.Run("Struct", func(t *testing.T) {
t.Run("True", getTestFuncForVM(prog, true, stackitem.NewStruct(arr), 4)) t.Run("True", getTestFuncForVM(prog, true, stackitem.NewStruct(arr), 4))
t.Run("too big", getTestFuncForVM(prog, nil, stackitem.NewStruct(arr), maxu64Plus(4)))
t.Run("False", getTestFuncForVM(prog, false, stackitem.NewStruct(arr), 5)) t.Run("False", getTestFuncForVM(prog, false, stackitem.NewStruct(arr), 5))
}) })
t.Run("Buffer", func(t *testing.T) { t.Run("Buffer", func(t *testing.T) {
t.Run("True", getTestFuncForVM(prog, true, stackitem.NewBuffer([]byte{5, 5, 5}), 2)) t.Run("True", getTestFuncForVM(prog, true, stackitem.NewBuffer([]byte{5, 5, 5}), 2))
t.Run("too big", getTestFuncForVM(prog, nil, stackitem.NewBuffer([]byte{5, 5, 5}), maxu64Plus(2)))
t.Run("False", getTestFuncForVM(prog, false, stackitem.NewBuffer([]byte{5, 5, 5}), 3)) t.Run("False", getTestFuncForVM(prog, false, stackitem.NewBuffer([]byte{5, 5, 5}), 3))
t.Run("Negative", getTestFuncForVM(prog, nil, stackitem.NewBuffer([]byte{5, 5, 5}), -1)) t.Run("Negative", getTestFuncForVM(prog, nil, stackitem.NewBuffer([]byte{5, 5, 5}), -1))
}) })
@ -1497,6 +1502,7 @@ func TestPICK(t *testing.T) {
prog := makeProgram(opcode.PICK) prog := makeProgram(opcode.PICK)
t.Run("NoItem", getTestFuncForVM(prog, nil, 1)) t.Run("NoItem", getTestFuncForVM(prog, nil, 1))
t.Run("Negative", getTestFuncForVM(prog, nil, -1)) t.Run("Negative", getTestFuncForVM(prog, nil, -1))
t.Run("very big", getTestFuncForVM(prog, nil, 1, 2, 3, maxu64Plus(1)))
} }
func TestPICKgood(t *testing.T) { func TestPICKgood(t *testing.T) {
@ -1557,6 +1563,17 @@ func TestROLLBad2(t *testing.T) {
runWithArgs(t, prog, nil, 1, 2, 3, 3) runWithArgs(t, prog, nil, 1, 2, 3, 3)
} }
func maxu64Plus(x int64) *big.Int {
bi := new(big.Int).SetUint64(math.MaxUint64)
bi.Add(bi, big.NewInt(2))
return bi
}
func TestROLLBad3(t *testing.T) {
prog := makeProgram(opcode.ROLL)
runWithArgs(t, prog, nil, 1, 2, 3, maxu64Plus(2))
}
func TestROLLGood(t *testing.T) { func TestROLLGood(t *testing.T) {
prog := makeProgram(opcode.ROLL) prog := makeProgram(opcode.ROLL)
vm := load(prog) vm := load(prog)
@ -1602,6 +1619,7 @@ func TestREVERSEN(t *testing.T) {
t.Run("Zero", getCustomTestFuncForVM(prog, getCheckEStackFunc(3, 2, 1), 1, 2, 3, 0)) t.Run("Zero", getCustomTestFuncForVM(prog, getCheckEStackFunc(3, 2, 1), 1, 2, 3, 0))
t.Run("OneItem", getCustomTestFuncForVM(prog, getCheckEStackFunc(42), 42, 1)) t.Run("OneItem", getCustomTestFuncForVM(prog, getCheckEStackFunc(42), 42, 1))
t.Run("Good", getCustomTestFuncForVM(prog, getCheckEStackFunc(1, 2, 3, 4, 5), 1, 2, 3, 4, 5, 5)) t.Run("Good", getCustomTestFuncForVM(prog, getCheckEStackFunc(1, 2, 3, 4, 5), 1, 2, 3, 4, 5, 5))
t.Run("VeryBigNumber", getCustomTestFuncForVM(prog, nil, 1, 2, maxu64Plus(2)))
} }
func TestTUCKbadNoitems(t *testing.T) { func TestTUCKbadNoitems(t *testing.T) {
@ -1695,6 +1713,7 @@ func TestXDROP(t *testing.T) {
prog := makeProgram(opcode.XDROP) prog := makeProgram(opcode.XDROP)
t.Run("NoArgument", getTestFuncForVM(prog, nil)) t.Run("NoArgument", getTestFuncForVM(prog, nil))
t.Run("NoN", getTestFuncForVM(prog, nil, 1, 2)) t.Run("NoN", getTestFuncForVM(prog, nil, 1, 2))
t.Run("very big argument", getTestFuncForVM(prog, nil, 1, 2, maxu64Plus(1)))
t.Run("Negative", getTestFuncForVM(prog, nil, 1, -1)) t.Run("Negative", getTestFuncForVM(prog, nil, 1, -1))
} }
@ -1780,7 +1799,9 @@ func TestSUBSTR(t *testing.T) {
t.Run("TwoArguments", getTestFuncForVM(prog, nil, 0, 2)) t.Run("TwoArguments", getTestFuncForVM(prog, nil, 0, 2))
t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte("bc")), []byte("abcdef"), 1, 2)) t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte("bc")), []byte("abcdef"), 1, 2))
t.Run("BadOffset", getTestFuncForVM(prog, nil, []byte("abcdef"), 7, 1)) t.Run("BadOffset", getTestFuncForVM(prog, nil, []byte("abcdef"), 7, 1))
t.Run("very big offset", getTestFuncForVM(prog, nil, []byte("abcdef"), maxu64Plus(1), 1))
t.Run("BigLen", getTestFuncForVM(prog, nil, []byte("abcdef"), 1, 6)) t.Run("BigLen", getTestFuncForVM(prog, nil, []byte("abcdef"), 1, 6))
t.Run("very big len", getTestFuncForVM(prog, nil, []byte("abcdef"), 1, maxu64Plus(1)))
t.Run("NegativeOffset", getTestFuncForVM(prog, nil, []byte("abcdef"), -1, 3)) t.Run("NegativeOffset", getTestFuncForVM(prog, nil, []byte("abcdef"), -1, 3))
t.Run("NegativeLen", getTestFuncForVM(prog, nil, []byte("abcdef"), 3, -1)) t.Run("NegativeLen", getTestFuncForVM(prog, nil, []byte("abcdef"), 3, -1))
} }
@ -1803,6 +1824,7 @@ func TestLEFT(t *testing.T) {
t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1)) t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1))
t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte("ab")), "abcdef", 2)) t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte("ab")), "abcdef", 2))
t.Run("BadBigLen", getTestFuncForVM(prog, nil, "abcdef", 8)) t.Run("BadBigLen", getTestFuncForVM(prog, nil, "abcdef", 8))
t.Run("bad, very big len", getTestFuncForVM(prog, nil, "abcdef", maxu64Plus(2)))
} }
func TestRIGHT(t *testing.T) { func TestRIGHT(t *testing.T) {
@ -1812,6 +1834,7 @@ func TestRIGHT(t *testing.T) {
t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1)) t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1))
t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte("ef")), "abcdef", 2)) t.Run("Good", getTestFuncForVM(prog, stackitem.NewBuffer([]byte("ef")), "abcdef", 2))
t.Run("BadLen", getTestFuncForVM(prog, nil, "abcdef", 8)) t.Run("BadLen", getTestFuncForVM(prog, nil, "abcdef", 8))
t.Run("bad, very big len", getTestFuncForVM(prog, nil, "abcdef", maxu64Plus(2)))
} }
func TestPACK(t *testing.T) { func TestPACK(t *testing.T) {
@ -2059,6 +2082,7 @@ func TestREMOVE(t *testing.T) {
t.Run("OneArgument", getTestFuncForVM(prog, nil, 1)) t.Run("OneArgument", getTestFuncForVM(prog, nil, 1))
t.Run("NotArray", getTestFuncForVM(prog, nil, 1, 1)) t.Run("NotArray", getTestFuncForVM(prog, nil, 1, 1))
t.Run("BadIndex", getTestFuncForVM(prog, nil, []int{22, 34, 42, 55, 81}, 10)) t.Run("BadIndex", getTestFuncForVM(prog, nil, []int{22, 34, 42, 55, 81}, 10))
t.Run("very big index", getTestFuncForVM(prog, nil, []int{22, 34, 42, 55, 81}, maxu64Plus(1)))
} }
func TestREMOVEGood(t *testing.T) { func TestREMOVEGood(t *testing.T) {