stackitem: rework struct cloning protection
Count everything, fail early, make it more compatible with neo-project/neo-vm#423.
This commit is contained in:
parent
79b1bf72aa
commit
1568ebc513
3 changed files with 23 additions and 18 deletions
|
@ -23,7 +23,9 @@ const (
|
||||||
// MaxSize is the maximum item size allowed in the VM.
|
// MaxSize is the maximum item size allowed in the VM.
|
||||||
MaxSize = 1024 * 1024
|
MaxSize = 1024 * 1024
|
||||||
// MaxComparableNumOfItems is the maximum number of items that can be compared for structs.
|
// MaxComparableNumOfItems is the maximum number of items that can be compared for structs.
|
||||||
MaxComparableNumOfItems = 2048
|
MaxComparableNumOfItems = MaxDeserialized
|
||||||
|
// MaxClonableNumOfItems is the maximum number of items that can be cloned in structs.
|
||||||
|
MaxClonableNumOfItems = MaxDeserialized
|
||||||
// MaxByteArrayComparableSize is the maximum allowed length of ByteArray for Equals method.
|
// MaxByteArrayComparableSize is the maximum allowed length of ByteArray for Equals method.
|
||||||
// It is set to be the maximum uint16 value.
|
// It is set to be the maximum uint16 value.
|
||||||
MaxByteArrayComparableSize = math.MaxUint16
|
MaxByteArrayComparableSize = math.MaxUint16
|
||||||
|
@ -323,13 +325,18 @@ func (i *Struct) Convert(typ Type) (Item, error) {
|
||||||
|
|
||||||
// Clone returns a Struct with all Struct fields copied by value.
|
// Clone returns a Struct with all Struct fields copied by value.
|
||||||
// Array fields are still copied by reference.
|
// Array fields are still copied by reference.
|
||||||
func (i *Struct) Clone(limit int) (*Struct, error) {
|
func (i *Struct) Clone() (*Struct, error) {
|
||||||
|
var limit = MaxClonableNumOfItems - 1 // For this struct itself.
|
||||||
return i.clone(&limit)
|
return i.clone(&limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Struct) clone(limit *int) (*Struct, error) {
|
func (i *Struct) clone(limit *int) (*Struct, error) {
|
||||||
ret := &Struct{make([]Item, len(i.value))}
|
ret := &Struct{make([]Item, len(i.value))}
|
||||||
for j := range i.value {
|
for j := range i.value {
|
||||||
|
*limit--
|
||||||
|
if *limit < 0 {
|
||||||
|
return nil, ErrTooBig
|
||||||
|
}
|
||||||
switch t := i.value[j].(type) {
|
switch t := i.value[j].(type) {
|
||||||
case *Struct:
|
case *Struct:
|
||||||
var err error
|
var err error
|
||||||
|
@ -338,13 +345,9 @@ func (i *Struct) clone(limit *int) (*Struct, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
*limit--
|
|
||||||
default:
|
default:
|
||||||
ret.value[j] = t
|
ret.value[j] = t
|
||||||
}
|
}
|
||||||
if *limit < 0 {
|
|
||||||
return nil, ErrTooBig
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -397,7 +397,7 @@ func TestEqualsDeepStructure(t *testing.T) {
|
||||||
var layerUp = func(sa *Struct, num int) (*Struct, int) {
|
var layerUp = func(sa *Struct, num int) (*Struct, int) {
|
||||||
items := []Item{}
|
items := []Item{}
|
||||||
for i := 0; i < perStruct; i++ {
|
for i := 0; i < perStruct; i++ {
|
||||||
clon, err := sa.Clone(100500)
|
clon, err := sa.Clone()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
items = append(items, clon)
|
items = append(items, clon)
|
||||||
}
|
}
|
||||||
|
@ -410,14 +410,13 @@ func TestEqualsDeepStructure(t *testing.T) {
|
||||||
sa, num = layerUp(sa, num)
|
sa, num = layerUp(sa, num)
|
||||||
}
|
}
|
||||||
require.Less(t, num, MaxComparableNumOfItems)
|
require.Less(t, num, MaxComparableNumOfItems)
|
||||||
sb, err := sa.Clone(num)
|
sb, err := sa.Clone()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, sa.Equals(sb))
|
require.True(t, sa.Equals(sb))
|
||||||
sa, num = layerUp(sa, num)
|
sa, num = layerUp(sa, num)
|
||||||
|
sb, num = layerUp(sb, num)
|
||||||
|
|
||||||
require.Less(t, MaxComparableNumOfItems, num)
|
require.Less(t, MaxComparableNumOfItems, num)
|
||||||
sb, err = sa.Clone(num)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Panics(t, func() { sa.Equals(sb) })
|
require.Panics(t, func() { sa.Equals(sb) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,9 +507,12 @@ func TestNewVeryBigInteger(t *testing.T) {
|
||||||
func TestStructClone(t *testing.T) {
|
func TestStructClone(t *testing.T) {
|
||||||
st0 := Struct{}
|
st0 := Struct{}
|
||||||
st := Struct{value: []Item{&st0}}
|
st := Struct{value: []Item{&st0}}
|
||||||
_, err := st.Clone(1)
|
for i := 0; i < MaxClonableNumOfItems-1; i++ {
|
||||||
require.NoError(t, err)
|
nst, err := st.Clone()
|
||||||
_, err = st.Clone(0)
|
require.NoError(t, err)
|
||||||
|
st = Struct{value: []Item{nst}}
|
||||||
|
}
|
||||||
|
_, err := st.Clone()
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
pkg/vm/vm.go
10
pkg/vm/vm.go
|
@ -1052,7 +1052,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
itemElem := v.estack.Pop()
|
itemElem := v.estack.Pop()
|
||||||
arrElem := v.estack.Pop()
|
arrElem := v.estack.Pop()
|
||||||
|
|
||||||
val := cloneIfStruct(itemElem.value, MaxStackSize-v.refs.size)
|
val := cloneIfStruct(itemElem.value)
|
||||||
|
|
||||||
switch t := arrElem.value.(type) {
|
switch t := arrElem.value.(type) {
|
||||||
case *stackitem.Array:
|
case *stackitem.Array:
|
||||||
|
@ -1358,12 +1358,12 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
src := t.Value().([]stackitem.Item)
|
src := t.Value().([]stackitem.Item)
|
||||||
arr = make([]stackitem.Item, len(src))
|
arr = make([]stackitem.Item, len(src))
|
||||||
for i := range src {
|
for i := range src {
|
||||||
arr[i] = cloneIfStruct(src[i], MaxStackSize-v.refs.size)
|
arr[i] = cloneIfStruct(src[i])
|
||||||
}
|
}
|
||||||
case *stackitem.Map:
|
case *stackitem.Map:
|
||||||
arr = make([]stackitem.Item, 0, t.Len())
|
arr = make([]stackitem.Item, 0, t.Len())
|
||||||
for k := range t.Value().([]stackitem.MapElement) {
|
for k := range t.Value().([]stackitem.MapElement) {
|
||||||
arr = append(arr, cloneIfStruct(t.Value().([]stackitem.MapElement)[k].Value, MaxStackSize-v.refs.size))
|
arr = append(arr, cloneIfStruct(t.Value().([]stackitem.MapElement)[k].Value))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("not a Map, Array or Struct")
|
panic("not a Map, Array or Struct")
|
||||||
|
@ -1729,10 +1729,10 @@ func checkMultisig1(v *VM, curve elliptic.Curve, h []byte, pkeys [][]byte, sig [
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func cloneIfStruct(item stackitem.Item, limit int) stackitem.Item {
|
func cloneIfStruct(item stackitem.Item) stackitem.Item {
|
||||||
switch it := item.(type) {
|
switch it := item.(type) {
|
||||||
case *stackitem.Struct:
|
case *stackitem.Struct:
|
||||||
ret, err := it.Clone(limit)
|
ret, err := it.Clone()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue