From fbe8bd2d9c5c829622873d1fce597a5d1192ddee Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 16 Jul 2021 12:28:11 +0300 Subject: [PATCH] stackitem: limit deserialized arrays/maps See neo-project/neo#2531, even though they use MaxStackSize there resulting element is not valid unless it has <=MaxArraySize elements. --- pkg/vm/stackitem/item.go | 1 + pkg/vm/stackitem/serialization.go | 8 ++++++++ pkg/vm/stackitem/serialization_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/pkg/vm/stackitem/item.go b/pkg/vm/stackitem/item.go index c5d7007fe..4db99c9fc 100644 --- a/pkg/vm/stackitem/item.go +++ b/pkg/vm/stackitem/item.go @@ -63,6 +63,7 @@ var ( // value exceeds MaxSize. ErrTooBig = errors.New("too big") + errTooBigArray = fmt.Errorf("%w: array", ErrTooBig) errTooBigComparable = fmt.Errorf("%w: uncomparable", ErrTooBig) errTooBigInteger = fmt.Errorf("%w: integer", ErrTooBig) errTooBigKey = fmt.Errorf("%w: map key", ErrTooBig) diff --git a/pkg/vm/stackitem/serialization.go b/pkg/vm/stackitem/serialization.go index b3e1b9407..67929e7eb 100644 --- a/pkg/vm/stackitem/serialization.go +++ b/pkg/vm/stackitem/serialization.go @@ -210,6 +210,10 @@ func decodeBinary(r *io.BinReader, allowInvalid bool) Item { return NewBigInteger(num) case ArrayT, StructT: size := int(r.ReadVarUint()) + if size > MaxArraySize { + r.Err = errTooBigArray + return nil + } arr := make([]Item, size) for i := 0; i < size; i++ { arr[i] = decodeBinary(r, allowInvalid) @@ -221,6 +225,10 @@ func decodeBinary(r *io.BinReader, allowInvalid bool) Item { return NewStruct(arr) case MapT: size := int(r.ReadVarUint()) + if size > MaxArraySize { + r.Err = errTooBigArray + return nil + } m := NewMap() for i := 0; i < size; i++ { key := decodeBinary(r, allowInvalid) diff --git a/pkg/vm/stackitem/serialization_test.go b/pkg/vm/stackitem/serialization_test.go index b5adff651..e93a59e21 100644 --- a/pkg/vm/stackitem/serialization_test.go +++ b/pkg/vm/stackitem/serialization_test.go @@ -39,6 +39,7 @@ func testSerialize(t *testing.T, expectedErr error, item Item) { func TestSerialize(t *testing.T) { bigByteArray := NewByteArray(make([]byte, MaxSize/2)) smallByteArray := NewByteArray(make([]byte, MaxSize/4)) + zeroByteArray := NewByteArray(make([]byte, 0)) testArray := func(t *testing.T, newItem func([]Item) Item) { arr := newItem([]Item{bigByteArray}) testSerialize(t, nil, arr) @@ -50,6 +51,18 @@ func TestSerialize(t *testing.T) { arr.Value().([]Item)[0] = arr testSerialize(t, ErrRecursive, arr) + + items := make([]Item, 0, MaxArraySize) + for i := 0; i < MaxArraySize; i++ { + items = append(items, zeroByteArray) + } + testSerialize(t, nil, newItem(items)) + + items = append(items, zeroByteArray) + data, err := Serialize(newItem(items)) + require.NoError(t, err) + _, err = Deserialize(data) + require.True(t, errors.Is(err, ErrTooBig), err) } t.Run("array", func(t *testing.T) { testArray(t, func(items []Item) Item { return NewArray(items) }) @@ -126,6 +139,18 @@ func TestSerialize(t *testing.T) { m.Add(Make(0), NewByteArray(make([]byte, MaxSize-MaxKeySize))) m.Add(NewByteArray(make([]byte, MaxKeySize)), Make(1)) testSerialize(t, ErrTooBig, m) + + m = NewMap() + for i := 0; i < MaxArraySize; i++ { + m.Add(Make(i), zeroByteArray) + } + testSerialize(t, nil, m) + + m.Add(Make(100500), zeroByteArray) + data, err := Serialize(m) + require.NoError(t, err) + _, err = Deserialize(data) + require.True(t, errors.Is(err, ErrTooBig), err) }) }