diff --git a/pkg/vm/stackitem/serialization.go b/pkg/vm/stackitem/serialization.go index 03cfa8ee1..f739817c3 100644 --- a/pkg/vm/stackitem/serialization.go +++ b/pkg/vm/stackitem/serialization.go @@ -232,6 +232,27 @@ func Deserialize(data []byte) (Item, error) { return item, nil } +// DeserializeLimited returns Item deserialized from the given byte slice. limit +// restricts the maximum number of items deserialized item can contain (including +// itself). The default limit of MaxDeserialized is used if non-positive limit is +// specified. +func DeserializeLimited(data []byte, limit int) (Item, error) { + r := io.NewBinReaderFromBuf(data) + dc := deserContext{ + BinReader: r, + allowInvalid: false, + limit: MaxDeserialized, + } + if limit > 0 { + dc.limit = limit + } + item := dc.decodeBinary() + if r.Err != nil { + return nil, r.Err + } + return item, nil +} + // DecodeBinary decodes the previously serialized Item from the given // reader. It's similar to the io.Serializable's DecodeBinary() but implemented // as a function because Item itself is an interface. Caveat: always check diff --git a/pkg/vm/stackitem/serialization_test.go b/pkg/vm/stackitem/serialization_test.go index 8378536d1..3c0b64190 100644 --- a/pkg/vm/stackitem/serialization_test.go +++ b/pkg/vm/stackitem/serialization_test.go @@ -209,6 +209,26 @@ func TestDeserializeTooManyElements(t *testing.T) { require.True(t, errors.Is(err, ErrTooBig), err) } +func TestDeserializeLimited(t *testing.T) { + customLimit := MaxDeserialized + 1 + item := Make(0) + for i := 0; i < customLimit-1; i++ { // 1 for zero inner element. + item = Make([]Item{item}) + } + data, err := Serialize(item) + require.NoError(t, err) + actual, err := DeserializeLimited(data, customLimit) + require.NoError(t, err) + require.Equal(t, item, actual) + + item = Make([]Item{item}) + data, err = Serialize(item) + require.NoError(t, err) + _, err = DeserializeLimited(data, customLimit) + require.Error(t, err) + require.True(t, errors.Is(err, ErrTooBig), err) +} + func BenchmarkEncodeBinary(b *testing.B) { arr := getBigArray(15)