Merge pull request #2054 from nspcc-dev/fix-oom-on-struct-cloning
Fix OOM on struct cloning
This commit is contained in:
commit
9de5c850e7
4 changed files with 54 additions and 8 deletions
|
@ -298,17 +298,30 @@ func (i *Struct) Convert(typ Type) (Item, error) {
|
|||
|
||||
// Clone returns a Struct with all Struct fields copied by value.
|
||||
// Array fields are still copied by reference.
|
||||
func (i *Struct) Clone() *Struct {
|
||||
func (i *Struct) Clone(limit int) (*Struct, error) {
|
||||
return i.clone(&limit)
|
||||
}
|
||||
|
||||
func (i *Struct) clone(limit *int) (*Struct, error) {
|
||||
ret := &Struct{make([]Item, len(i.value))}
|
||||
for j := range i.value {
|
||||
switch t := i.value[j].(type) {
|
||||
case *Struct:
|
||||
ret.value[j] = t.Clone()
|
||||
var err error
|
||||
|
||||
ret.value[j], err = t.clone(limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*limit--
|
||||
default:
|
||||
ret.value[j] = t
|
||||
}
|
||||
if *limit < 0 {
|
||||
return nil, ErrTooBig
|
||||
}
|
||||
}
|
||||
return ret
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Null represents null on the stack.
|
||||
|
|
|
@ -465,6 +465,15 @@ func TestNewVeryBigInteger(t *testing.T) {
|
|||
check(false, new(big.Int).Mul(maxBitSet, big.NewInt(2)))
|
||||
}
|
||||
|
||||
func TestStructClone(t *testing.T) {
|
||||
st0 := Struct{}
|
||||
st := Struct{value: []Item{&st0}}
|
||||
_, err := st.Clone(1)
|
||||
require.NoError(t, err)
|
||||
_, err = st.Clone(0)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestDeepCopy(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
|
14
pkg/vm/vm.go
14
pkg/vm/vm.go
|
@ -1056,7 +1056,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
itemElem := v.estack.Pop()
|
||||
arrElem := v.estack.Pop()
|
||||
|
||||
val := cloneIfStruct(itemElem.value)
|
||||
val := cloneIfStruct(itemElem.value, MaxStackSize-v.refs.size)
|
||||
|
||||
switch t := arrElem.value.(type) {
|
||||
case *stackitem.Array:
|
||||
|
@ -1370,12 +1370,12 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
src := t.Value().([]stackitem.Item)
|
||||
arr = make([]stackitem.Item, len(src))
|
||||
for i := range src {
|
||||
arr[i] = cloneIfStruct(src[i])
|
||||
arr[i] = cloneIfStruct(src[i], MaxStackSize-v.refs.size)
|
||||
}
|
||||
case *stackitem.Map:
|
||||
arr = make([]stackitem.Item, 0, t.Len())
|
||||
for k := range t.Value().([]stackitem.MapElement) {
|
||||
arr = append(arr, cloneIfStruct(t.Value().([]stackitem.MapElement)[k].Value))
|
||||
arr = append(arr, cloneIfStruct(t.Value().([]stackitem.MapElement)[k].Value, MaxStackSize-v.refs.size))
|
||||
}
|
||||
default:
|
||||
panic("not a Map, Array or Struct")
|
||||
|
@ -1741,10 +1741,14 @@ func checkMultisig1(v *VM, curve elliptic.Curve, h []byte, pkeys [][]byte, sig [
|
|||
return false
|
||||
}
|
||||
|
||||
func cloneIfStruct(item stackitem.Item) stackitem.Item {
|
||||
func cloneIfStruct(item stackitem.Item, limit int) stackitem.Item {
|
||||
switch it := item.(type) {
|
||||
case *stackitem.Struct:
|
||||
return it.Clone()
|
||||
ret, err := it.Clone(limit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ret
|
||||
default:
|
||||
return it
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package vm
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
@ -2431,6 +2432,25 @@ func TestSLOTOpcodes(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestNestedStructClone(t *testing.T) {
|
||||
progs := []string{
|
||||
// VALUES for deeply nested structs, see neo-project/neo#2534.
|
||||
"5601c501fe0360589d604a12c0db415824f7cd45",
|
||||
// APPEND of deeply nested struct to empty array.
|
||||
"5601c2c501fe0360589d604a12c0db415824f7cf45",
|
||||
// VALUES for map with deeply nested struct.
|
||||
"5601c84a11c501fe0060589d604a12c0db415824f7d0cd45",
|
||||
// VALUES for a lot of not-so-deep nested structs.
|
||||
"5601c5000a60589d604a12c0db415824f701fe03504a519d4a102afa01ff03c0cd45",
|
||||
}
|
||||
for _, h := range progs {
|
||||
prog, err := hex.DecodeString(h)
|
||||
require.NoError(t, err)
|
||||
vm := load(prog)
|
||||
checkVMFailed(t, vm)
|
||||
}
|
||||
}
|
||||
|
||||
func makeProgram(opcodes ...opcode.Opcode) []byte {
|
||||
prog := make([]byte, len(opcodes)+1) // RET
|
||||
for i := 0; i < len(opcodes); i++ {
|
||||
|
|
Loading…
Reference in a new issue