diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 451b0b023..ff9f6973c 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "math" "math/rand" "github.com/nspcc-dev/neo-go/pkg/config/netmode" @@ -14,6 +15,8 @@ import ( ) const ( + // MaxScriptLength is the limit for transaction's script length. + MaxScriptLength = math.MaxUint16 // MaxTransactionSize is the upper limit size in bytes that a transaction can reach. It is // set to be 102400. MaxTransactionSize = 102400 @@ -140,7 +143,7 @@ func (t *Transaction) decodeHashableFields(br *io.BinReader) { t.ValidUntilBlock = br.ReadU32LE() br.ReadArray(&t.Signers, MaxAttributes) br.ReadArray(&t.Attributes, MaxAttributes-len(t.Signers)) - t.Script = br.ReadVarBytes() + t.Script = br.ReadVarBytes(MaxScriptLength) if br.Err == nil { br.Err = t.isValid() } diff --git a/pkg/core/transaction/witness.go b/pkg/core/transaction/witness.go index 714390f5d..57f6d634a 100644 --- a/pkg/core/transaction/witness.go +++ b/pkg/core/transaction/witness.go @@ -6,6 +6,16 @@ import ( "github.com/nspcc-dev/neo-go/pkg/util" ) +const ( + // MaxInvocationScript is the maximum length of allowed invocation + // script. It should fit 11/21 multisignature for the committee. + MaxInvocationScript = 1024 + + // MaxVerificationScript is the maximum allowed length of verification + // script. It should be appropriate for 11/21 multisignature committee. + MaxVerificationScript = 1024 +) + // Witness contains 2 scripts. type Witness struct { InvocationScript []byte `json:"invocation"` @@ -14,8 +24,8 @@ type Witness struct { // DecodeBinary implements Serializable interface. func (w *Witness) DecodeBinary(br *io.BinReader) { - w.InvocationScript = br.ReadVarBytes() - w.VerificationScript = br.ReadVarBytes() + w.InvocationScript = br.ReadVarBytes(MaxInvocationScript) + w.VerificationScript = br.ReadVarBytes(MaxVerificationScript) } // EncodeBinary implements Serializable interface. diff --git a/pkg/core/transaction/witness_test.go b/pkg/core/transaction/witness_test.go new file mode 100644 index 000000000..ac135e11d --- /dev/null +++ b/pkg/core/transaction/witness_test.go @@ -0,0 +1,38 @@ +package transaction + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/pkg/internal/testserdes" + "github.com/stretchr/testify/require" +) + +func TestWitnessSerDes(t *testing.T) { + var good1 = &Witness{ + InvocationScript: make([]byte, 64), + VerificationScript: make([]byte, 32), + } + var good2 = &Witness{ + InvocationScript: make([]byte, MaxInvocationScript), + VerificationScript: make([]byte, MaxVerificationScript), + } + var bad1 = &Witness{ + InvocationScript: make([]byte, MaxInvocationScript+1), + VerificationScript: make([]byte, 32), + } + var bad2 = &Witness{ + InvocationScript: make([]byte, 128), + VerificationScript: make([]byte, MaxVerificationScript+1), + } + var exp = new(Witness) + testserdes.MarshalUnmarshalJSON(t, good1, exp) + testserdes.MarshalUnmarshalJSON(t, good2, exp) + testserdes.EncodeDecodeBinary(t, good1, exp) + testserdes.EncodeDecodeBinary(t, good2, exp) + bin1, err := testserdes.EncodeBinary(bad1) + require.NoError(t, err) + bin2, err := testserdes.EncodeBinary(bad2) + require.NoError(t, err) + require.Error(t, testserdes.DecodeBinary(bin1, exp)) + require.Error(t, testserdes.DecodeBinary(bin2, exp)) +} diff --git a/pkg/network/payload/address.go b/pkg/network/payload/address.go index 6608b2cf6..13e9e9743 100644 --- a/pkg/network/payload/address.go +++ b/pkg/network/payload/address.go @@ -10,6 +10,10 @@ import ( "github.com/nspcc-dev/neo-go/pkg/network/capability" ) +// MaxAddrsCount is the maximum number of addresses that could be packed into +// one payload. +const MaxAddrsCount = 200 + // AddressAndTime payload. type AddressAndTime struct { Timestamp uint32 @@ -75,7 +79,10 @@ func NewAddressList(n int) *AddressList { // DecodeBinary implements Serializable interface. func (p *AddressList) DecodeBinary(br *io.BinReader) { - br.ReadArray(&p.Addrs) + br.ReadArray(&p.Addrs, MaxAddrsCount) + if len(p.Addrs) == 0 { + br.Err = errors.New("no addresses listed") + } } // EncodeBinary implements Serializable interface. diff --git a/pkg/network/payload/address_test.go b/pkg/network/payload/address_test.go index 6aa29414c..b429a8a24 100644 --- a/pkg/network/payload/address_test.go +++ b/pkg/network/payload/address_test.go @@ -9,6 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/network/capability" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestEncodeDecodeAddress(t *testing.T) { @@ -36,18 +37,38 @@ func TestEncodeDecodeAddress(t *testing.T) { testserdes.EncodeDecodeBinary(t, addr, new(AddressAndTime)) } -func TestEncodeDecodeAddressList(t *testing.T) { - var lenList uint8 = 4 - addrList := NewAddressList(int(lenList)) - for i := 0; i < int(lenList); i++ { - e, _ := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:200%d", i)) - addrList.Addrs[i] = NewAddressAndTime(e, time.Now(), capability.Capabilities{ +func fillAddressList(al *AddressList) { + for i := 0; i < len(al.Addrs); i++ { + e, _ := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:20%d", i)) + al.Addrs[i] = NewAddressAndTime(e, time.Now(), capability.Capabilities{ { Type: capability.TCPServer, Data: &capability.Server{Port: 123}, }, }) } +} +func TestEncodeDecodeAddressList(t *testing.T) { + var lenList uint8 = 4 + addrList := NewAddressList(int(lenList)) + fillAddressList(addrList) testserdes.EncodeDecodeBinary(t, addrList, new(AddressList)) } + +func TestEncodeDecodeBadAddressList(t *testing.T) { + var newAL = new(AddressList) + addrList := NewAddressList(MaxAddrsCount + 1) + fillAddressList(addrList) + + bin, err := testserdes.EncodeBinary(addrList) + require.NoError(t, err) + err = testserdes.DecodeBinary(bin, newAL) + require.Error(t, err) + + addrList = NewAddressList(0) + bin, err = testserdes.EncodeBinary(addrList) + require.NoError(t, err) + err = testserdes.DecodeBinary(bin, newAL) + require.Error(t, err) +} diff --git a/pkg/network/payload/version.go b/pkg/network/payload/version.go index 6bb3bcdc1..dee8650a1 100644 --- a/pkg/network/payload/version.go +++ b/pkg/network/payload/version.go @@ -8,6 +8,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/network/capability" ) +// MaxUserAgentLength is the limit for user agent field. +const MaxUserAgentLength = 1024 + // Version payload. type Version struct { // NetMode of the node @@ -42,7 +45,7 @@ func (p *Version) DecodeBinary(br *io.BinReader) { p.Version = br.ReadU32LE() p.Timestamp = br.ReadU32LE() p.Nonce = br.ReadU32LE() - p.UserAgent = br.ReadVarBytes() + p.UserAgent = br.ReadVarBytes(MaxUserAgentLength) p.Capabilities.DecodeBinary(br) } diff --git a/pkg/network/server.go b/pkg/network/server.go index 467c128f3..e8feac478 100644 --- a/pkg/network/server.go +++ b/pkg/network/server.go @@ -29,7 +29,6 @@ const ( defaultAttemptConnPeers = 20 defaultMaxPeers = 100 maxBlockBatch = 200 - maxAddrsToSend = 200 minPoolCount = 30 ) @@ -690,8 +689,8 @@ func (s *Server) handleAddrCmd(p Peer, addrs *payload.AddressList) error { // handleGetAddrCmd sends to the peer some good addresses that we know of. func (s *Server) handleGetAddrCmd(p Peer) error { addrs := s.discovery.GoodPeers() - if len(addrs) > maxAddrsToSend { - addrs = addrs[:maxAddrsToSend] + if len(addrs) > payload.MaxAddrsCount { + addrs = addrs[:payload.MaxAddrsCount] } alist := payload.NewAddressList(len(addrs)) ts := time.Now() diff --git a/pkg/smartcontract/manifest/manifest.go b/pkg/smartcontract/manifest/manifest.go index d7a63aeed..40680dad1 100644 --- a/pkg/smartcontract/manifest/manifest.go +++ b/pkg/smartcontract/manifest/manifest.go @@ -184,7 +184,7 @@ func (m *Manifest) EncodeBinary(w *io.BinWriter) { // DecodeBinary implements io.Serializable. func (m *Manifest) DecodeBinary(r *io.BinReader) { - data := r.ReadVarBytes() + data := r.ReadVarBytes(MaxManifestSize) if r.Err != nil { return } else if err := json.Unmarshal(data, m); err != nil { diff --git a/pkg/smartcontract/nef/nef.go b/pkg/smartcontract/nef/nef.go index 9725e3df4..39f6433bf 100644 --- a/pkg/smartcontract/nef/nef.go +++ b/pkg/smartcontract/nef/nef.go @@ -203,11 +203,7 @@ func (n *File) DecodeBinary(r *io.BinReader) { r.Err = errors.New("CRC verification fail") return } - n.Script = r.ReadVarBytes() - if len(n.Script) > MaxScriptLength { - r.Err = errors.New("invalid script length") - return - } + n.Script = r.ReadVarBytes(MaxScriptLength) if !hash.Hash160(n.Script).Equals(n.Header.ScriptHash) { r.Err = errors.New("script hashes mismatch") return diff --git a/pkg/vm/stackitem/serialization.go b/pkg/vm/stackitem/serialization.go index b3b111a7d..f6b603718 100644 --- a/pkg/vm/stackitem/serialization.go +++ b/pkg/vm/stackitem/serialization.go @@ -98,13 +98,13 @@ func DecodeBinaryStackItem(r *io.BinReader) Item { switch t { case ByteArrayT, BufferT: - data := r.ReadVarBytes() + data := r.ReadVarBytes(MaxSize) return NewByteArray(data) case BooleanT: var b = r.ReadBool() return NewBool(b) case IntegerT: - data := r.ReadVarBytes() + data := r.ReadVarBytes(bigint.MaxBytesLen) num := bigint.FromBytes(data) return NewBigInteger(num) case ArrayT, StructT: