From b9ff07f32c18b125d140c9abbba66157a5d78acf Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 6 Jul 2021 19:34:02 +0300 Subject: [PATCH] stackitem: add limit to serialized items Standard binary serialization/deserialization is mostly used in VM to put/get elements into/from storage, so they never should exceed MaxSize (otherwise one won't be able to deserialize these items). This patch leaves EncodeBinaryStackItem unprotected, but that's a streaming interface, so it's up to the user of it to ensure its appropriate use (and our uses are mostly for native contract's data, so they're fine). --- pkg/vm/stackitem/serialization.go | 17 +++++++++++++---- pkg/vm/stackitem/serialization_test.go | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 pkg/vm/stackitem/serialization_test.go diff --git a/pkg/vm/stackitem/serialization.go b/pkg/vm/stackitem/serialization.go index 93b625095..f121a3659 100644 --- a/pkg/vm/stackitem/serialization.go +++ b/pkg/vm/stackitem/serialization.go @@ -12,6 +12,7 @@ import ( // serContext is an internal serialization context. type serContext struct { *io.BinWriter + buf *io.BufBinWriter allowInvalid bool seen map[Item]bool } @@ -21,6 +22,7 @@ func SerializeItem(item Item) ([]byte, error) { w := io.NewBufBinWriter() sc := serContext{ BinWriter: w.BinWriter, + buf: w, allowInvalid: false, seen: make(map[Item]bool), } @@ -49,6 +51,7 @@ func EncodeBinaryStackItemAppExec(item Item, w *io.BinWriter) { bw := io.NewBufBinWriter() sc := serContext{ BinWriter: bw.BinWriter, + buf: bw, allowInvalid: true, seen: make(map[Item]bool), } @@ -68,10 +71,6 @@ func (w *serContext) serialize(item Item) { w.Err = errors.New("recursive structures can't be serialized") return } - if item == nil && w.allowInvalid { - w.WriteBytes([]byte{byte(InvalidT)}) - return - } switch t := item.(type) { case *ByteArray: @@ -120,6 +119,16 @@ func (w *serContext) serialize(item Item) { delete(w.seen, item) case Null: w.WriteB(byte(AnyT)) + case nil: + if w.allowInvalid { + w.WriteBytes([]byte{byte(InvalidT)}) + } else { + w.Err = errors.New("invalid stack item") + } + } + + if w.Err == nil && w.buf != nil && w.buf.Len() > MaxSize { + w.Err = errors.New("too big item") } } diff --git a/pkg/vm/stackitem/serialization_test.go b/pkg/vm/stackitem/serialization_test.go new file mode 100644 index 000000000..26000a94a --- /dev/null +++ b/pkg/vm/stackitem/serialization_test.go @@ -0,0 +1,21 @@ +package stackitem + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSerializationMaxErr(t *testing.T) { + base := make([]byte, MaxSize/2+1) + item := Make(base) + + arr := []Item{item, item.Dup()} + aitem := Make(arr) + + _, err := SerializeItem(item) + require.NoError(t, err) + + _, err = SerializeItem(aitem) + require.Error(t, err) +}