From 3c5c687df9d79acb353fe462e86bae8c7be45b1d Mon Sep 17 00:00:00 2001 From: Nate Maninger Date: Thu, 6 Jun 2024 09:21:06 -0700 Subject: [PATCH] consensus,gateway,rhp,types: add limits to all prefixes --- consensus/state.go | 14 ++++---- gateway/encoding.go | 24 +++++++------- rhp/v2/encoding.go | 50 ++++++++++++++--------------- rhp/v3/encoding.go | 25 ++++++++------- rhp/v3/transport.go | 14 ++++---- types/encoding.go | 72 ++++++++++++++++++++++-------------------- types/encoding_test.go | 24 ++++++++++++-- types/multiproof.go | 2 +- 8 files changed, 127 insertions(+), 98 deletions(-) diff --git a/consensus/state.go b/consensus/state.go index ca6ae92b..0c272f57 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -14,6 +14,8 @@ import ( "go.sia.tech/core/types" ) +const maxTransactionElements = 10000 + // Pool for reducing heap allocations when hashing. This is only necessary // because blake2b.New256 returns a hash.Hash interface, which prevents the // compiler from doing escape analysis. Can be removed if we switch to an @@ -713,19 +715,19 @@ func (ts V1TransactionSupplement) EncodeTo(e *types.Encoder) { // DecodeFrom implements types.DecoderFrom. func (ts *V1TransactionSupplement) DecodeFrom(d *types.Decoder) { - ts.SiacoinInputs = make([]types.SiacoinElement, d.ReadPrefix()) + ts.SiacoinInputs = make([]types.SiacoinElement, d.ReadPrefix(maxTransactionElements)) for i := range ts.SiacoinInputs { ts.SiacoinInputs[i].DecodeFrom(d) } - ts.SiafundInputs = make([]types.SiafundElement, d.ReadPrefix()) + ts.SiafundInputs = make([]types.SiafundElement, d.ReadPrefix(maxTransactionElements)) for i := range ts.SiafundInputs { ts.SiafundInputs[i].DecodeFrom(d) } - ts.RevisedFileContracts = make([]types.FileContractElement, d.ReadPrefix()) + ts.RevisedFileContracts = make([]types.FileContractElement, d.ReadPrefix(maxTransactionElements)) for i := range ts.RevisedFileContracts { ts.RevisedFileContracts[i].DecodeFrom(d) } - ts.ValidFileContracts = make([]types.FileContractElement, d.ReadPrefix()) + ts.ValidFileContracts = make([]types.FileContractElement, d.ReadPrefix(maxTransactionElements)) for i := range ts.ValidFileContracts { ts.ValidFileContracts[i].DecodeFrom(d) } @@ -795,11 +797,11 @@ func (bs V1BlockSupplement) EncodeTo(e *types.Encoder) { // DecodeFrom implements types.DecoderFrom. func (bs *V1BlockSupplement) DecodeFrom(d *types.Decoder) { - bs.Transactions = make([]V1TransactionSupplement, d.ReadPrefix()) + bs.Transactions = make([]V1TransactionSupplement, d.ReadPrefix(maxTransactionElements)) for i := range bs.Transactions { bs.Transactions[i].DecodeFrom(d) } - bs.ExpiringFileContracts = make([]types.FileContractElement, d.ReadPrefix()) + bs.ExpiringFileContracts = make([]types.FileContractElement, d.ReadPrefix(1000000)) // arbitrary for i := range bs.ExpiringFileContracts { bs.ExpiringFileContracts[i].DecodeFrom(d) } diff --git a/gateway/encoding.go b/gateway/encoding.go index cc74fec5..1b7121c8 100644 --- a/gateway/encoding.go +++ b/gateway/encoding.go @@ -24,7 +24,7 @@ func withV1Encoder(w io.Writer, fn func(*types.Encoder)) error { func withV1Decoder(r io.Reader, maxLen int, fn func(*types.Decoder)) error { d := types.NewDecoder(io.LimitedReader{R: r, N: int64(8 + maxLen)}) - d.ReadPrefix() // ignored + d.ReadPrefix(int64(maxLen)) // ignored fn(d) return d.Err() } @@ -128,13 +128,13 @@ func (ob *V2BlockOutline) decodeFrom(d *types.Decoder) { ob.Timestamp = d.ReadTime() ob.MinerAddress.DecodeFrom(d) - txns := make([]types.Transaction, d.ReadPrefix()) + txns := make([]types.Transaction, d.ReadPrefix(10000)) for i := range txns { txns[i].DecodeFrom(d) } var v2txns types.V2TransactionsMultiproof v2txns.DecodeFrom(d) - hashes := make([]types.Hash256, d.ReadPrefix()) + hashes := make([]types.Hash256, d.ReadPrefix(100000)) for i := range hashes { hashes[i].DecodeFrom(d) } @@ -205,7 +205,7 @@ func (r *RPCShareNodes) encodeResponse(e *types.Encoder) { } } func (r *RPCShareNodes) decodeResponse(d *types.Decoder) { - r.Peers = make([]string, d.ReadPrefix()) + r.Peers = make([]string, d.ReadPrefix(100)) for i := range r.Peers { r.Peers[i] = d.ReadString() } @@ -247,7 +247,7 @@ func (r *RPCSendBlocks) encodeResponse(e *types.Encoder) { } } func (r *RPCSendBlocks) decodeResponse(d *types.Decoder) { - r.Blocks = make([]types.Block, d.ReadPrefix()) + r.Blocks = make([]types.Block, d.ReadPrefix(1000)) for i := range r.Blocks { (*types.V1Block)(&r.Blocks[i]).DecodeFrom(d) } @@ -304,7 +304,7 @@ func (r *RPCRelayTransactionSet) encodeRequest(e *types.Encoder) { } } func (r *RPCRelayTransactionSet) decodeRequest(d *types.Decoder) { - r.Transactions = make([]types.Transaction, d.ReadPrefix()) + r.Transactions = make([]types.Transaction, d.ReadPrefix(1000)) for i := range r.Transactions { r.Transactions[i].DecodeFrom(d) } @@ -327,7 +327,7 @@ func (r *RPCSendV2Blocks) encodeRequest(e *types.Encoder) { e.WriteUint64(r.Max) } func (r *RPCSendV2Blocks) decodeRequest(d *types.Decoder) { - r.History = make([]types.BlockID, d.ReadPrefix()) + r.History = make([]types.BlockID, d.ReadPrefix(10000)) for i := range r.History { r.History[i].DecodeFrom(d) } @@ -343,7 +343,7 @@ func (r *RPCSendV2Blocks) encodeResponse(e *types.Encoder) { e.WriteUint64(r.Remaining) } func (r *RPCSendV2Blocks) decodeResponse(d *types.Decoder) { - r.Blocks = make([]types.Block, d.ReadPrefix()) + r.Blocks = make([]types.Block, d.ReadPrefix(1000)) for i := range r.Blocks { (*types.V2Block)(&r.Blocks[i]).DecodeFrom(d) } @@ -369,7 +369,7 @@ func (r *RPCSendTransactions) encodeRequest(e *types.Encoder) { } func (r *RPCSendTransactions) decodeRequest(d *types.Decoder) { r.Index.DecodeFrom(d) - r.Hashes = make([]types.Hash256, d.ReadPrefix()) + r.Hashes = make([]types.Hash256, d.ReadPrefix(1000)) for i := range r.Hashes { r.Hashes[i].DecodeFrom(d) } @@ -387,11 +387,11 @@ func (r *RPCSendTransactions) encodeResponse(e *types.Encoder) { } } func (r *RPCSendTransactions) decodeResponse(d *types.Decoder) { - r.Transactions = make([]types.Transaction, d.ReadPrefix()) + r.Transactions = make([]types.Transaction, d.ReadPrefix(10000)) for i := range r.Transactions { r.Transactions[i].DecodeFrom(d) } - r.V2Transactions = make([]types.V2Transaction, d.ReadPrefix()) + r.V2Transactions = make([]types.V2Transaction, d.ReadPrefix(10000)) for i := range r.V2Transactions { r.V2Transactions[i].DecodeFrom(d) } @@ -456,7 +456,7 @@ func (r *RPCRelayV2TransactionSet) encodeRequest(e *types.Encoder) { } func (r *RPCRelayV2TransactionSet) decodeRequest(d *types.Decoder) { r.Index.DecodeFrom(d) - r.Transactions = make([]types.V2Transaction, d.ReadPrefix()) + r.Transactions = make([]types.V2Transaction, d.ReadPrefix(5000)) for i := range r.Transactions { r.Transactions[i].DecodeFrom(d) } diff --git a/rhp/v2/encoding.go b/rhp/v2/encoding.go index a431377f..2c546661 100644 --- a/rhp/v2/encoding.go +++ b/rhp/v2/encoding.go @@ -59,7 +59,7 @@ func (r *loopKeyExchangeRequest) EncodeTo(e *types.Encoder) { func (r *loopKeyExchangeRequest) DecodeFrom(d *types.Decoder) { new(types.Specifier).DecodeFrom(d) // loopEnter d.Read(r.PublicKey[:]) - r.Ciphers = make([]types.Specifier, d.ReadPrefix()) + r.Ciphers = make([]types.Specifier, d.ReadPrefix(10)) for i := range r.Ciphers { r.Ciphers[i].DecodeFrom(d) } @@ -92,7 +92,7 @@ func (r *RPCFormContractRequest) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject. func (r *RPCFormContractRequest) DecodeFrom(d *types.Decoder) { - r.Transactions = make([]types.Transaction, d.ReadPrefix()) + r.Transactions = make([]types.Transaction, d.ReadPrefix(100)) for i := range r.Transactions { r.Transactions[i].DecodeFrom(d) } @@ -117,15 +117,15 @@ func (r *RPCFormContractAdditions) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject. func (r *RPCFormContractAdditions) DecodeFrom(d *types.Decoder) { - r.Parents = make([]types.Transaction, d.ReadPrefix()) + r.Parents = make([]types.Transaction, d.ReadPrefix(100)) for i := range r.Parents { r.Parents[i].DecodeFrom(d) } - r.Inputs = make([]types.SiacoinInput, d.ReadPrefix()) + r.Inputs = make([]types.SiacoinInput, d.ReadPrefix(100)) for i := range r.Inputs { r.Inputs[i].DecodeFrom(d) } - r.Outputs = make([]types.SiacoinOutput, d.ReadPrefix()) + r.Outputs = make([]types.SiacoinOutput, d.ReadPrefix(100)) for i := range r.Outputs { (*types.V1SiacoinOutput)(&r.Outputs[i]).DecodeFrom(d) } @@ -142,7 +142,7 @@ func (r *RPCFormContractSignatures) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject. func (r *RPCFormContractSignatures) DecodeFrom(d *types.Decoder) { - r.ContractSignatures = make([]types.TransactionSignature, d.ReadPrefix()) + r.ContractSignatures = make([]types.TransactionSignature, d.ReadPrefix(100)) for i := range r.ContractSignatures { r.ContractSignatures[i].DecodeFrom(d) } @@ -170,16 +170,16 @@ func (r *RPCRenewAndClearContractRequest) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject. func (r *RPCRenewAndClearContractRequest) DecodeFrom(d *types.Decoder) { - r.Transactions = make([]types.Transaction, d.ReadPrefix()) + r.Transactions = make([]types.Transaction, d.ReadPrefix(100)) for i := range r.Transactions { r.Transactions[i].DecodeFrom(d) } r.RenterKey.DecodeFrom(d) - r.FinalValidProofValues = make([]types.Currency, d.ReadPrefix()) + r.FinalValidProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.FinalValidProofValues { (*types.V1Currency)(&r.FinalValidProofValues[i]).DecodeFrom(d) } - r.FinalMissedProofValues = make([]types.Currency, d.ReadPrefix()) + r.FinalMissedProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.FinalMissedProofValues { (*types.V1Currency)(&r.FinalMissedProofValues[i]).DecodeFrom(d) } @@ -197,7 +197,7 @@ func (r *RPCRenewAndClearContractSignatures) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject. func (r *RPCRenewAndClearContractSignatures) DecodeFrom(d *types.Decoder) { - r.ContractSignatures = make([]types.TransactionSignature, d.ReadPrefix()) + r.ContractSignatures = make([]types.TransactionSignature, d.ReadPrefix(100)) for i := range r.ContractSignatures { r.ContractSignatures[i].DecodeFrom(d) } @@ -237,7 +237,7 @@ func (r *RPCLockResponse) DecodeFrom(d *types.Decoder) { r.Acquired = d.ReadBool() d.Read(r.NewChallenge[:]) r.Revision.DecodeFrom(d) - r.Signatures = make([]types.TransactionSignature, d.ReadPrefix()) + r.Signatures = make([]types.TransactionSignature, d.ReadPrefix(100)) for i := range r.Signatures { r.Signatures[i].DecodeFrom(d) } @@ -268,7 +268,7 @@ func (r *RPCReadRequest) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject. func (r *RPCReadRequest) DecodeFrom(d *types.Decoder) { - r.Sections = make([]RPCReadRequestSection, d.ReadPrefix()) + r.Sections = make([]RPCReadRequestSection, d.ReadPrefix(50)) for i := range r.Sections { d.Read(r.Sections[i].MerkleRoot[:]) r.Sections[i].Offset = d.ReadUint64() @@ -276,11 +276,11 @@ func (r *RPCReadRequest) DecodeFrom(d *types.Decoder) { } r.MerkleProof = d.ReadBool() r.RevisionNumber = d.ReadUint64() - r.ValidProofValues = make([]types.Currency, d.ReadPrefix()) + r.ValidProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.ValidProofValues { (*types.V1Currency)(&r.ValidProofValues[i]).DecodeFrom(d) } - r.MissedProofValues = make([]types.Currency, d.ReadPrefix()) + r.MissedProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.MissedProofValues { (*types.V1Currency)(&r.MissedProofValues[i]).DecodeFrom(d) } @@ -306,14 +306,14 @@ func (r *RPCReadResponse) DecodeFrom(d *types.Decoder) { // // NOTE: for maximum efficiency, we should be doing this for every slice, // but in most cases the extra performance isn't worth the aliasing issues. - dataLen := d.ReadPrefix() + dataLen := d.ReadPrefix(SectorSize) if cap(r.Data) < dataLen { r.Data = make([]byte, dataLen) } r.Data = r.Data[:dataLen] d.Read(r.Data) - r.MerkleProof = make([]types.Hash256, d.ReadPrefix()) + r.MerkleProof = make([]types.Hash256, d.ReadPrefix(10000)) for i := range r.MerkleProof { d.Read(r.MerkleProof[i][:]) } @@ -342,11 +342,11 @@ func (r *RPCSectorRootsRequest) DecodeFrom(d *types.Decoder) { r.RootOffset = d.ReadUint64() r.NumRoots = d.ReadUint64() r.RevisionNumber = d.ReadUint64() - r.ValidProofValues = make([]types.Currency, d.ReadPrefix()) + r.ValidProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.ValidProofValues { (*types.V1Currency)(&r.ValidProofValues[i]).DecodeFrom(d) } - r.MissedProofValues = make([]types.Currency, d.ReadPrefix()) + r.MissedProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.MissedProofValues { (*types.V1Currency)(&r.MissedProofValues[i]).DecodeFrom(d) } @@ -369,11 +369,11 @@ func (r *RPCSectorRootsResponse) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject. func (r *RPCSectorRootsResponse) DecodeFrom(d *types.Decoder) { copy(r.Signature[:], d.ReadBytes()) - r.SectorRoots = make([]types.Hash256, d.ReadPrefix()) + r.SectorRoots = make([]types.Hash256, d.ReadPrefix(15000000)) // ~50 TB of data or 500 MB of sector roots for i := range r.SectorRoots { d.Read(r.SectorRoots[i][:]) } - r.MerkleProof = make([]types.Hash256, d.ReadPrefix()) + r.MerkleProof = make([]types.Hash256, d.ReadPrefix(1_000_000)) for i := range r.MerkleProof { d.Read(r.MerkleProof[i][:]) } @@ -416,7 +416,7 @@ func (r *RPCWriteRequest) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject. func (r *RPCWriteRequest) DecodeFrom(d *types.Decoder) { - r.Actions = make([]RPCWriteAction, d.ReadPrefix()) + r.Actions = make([]RPCWriteAction, d.ReadPrefix(50)) for i := range r.Actions { d.Read(r.Actions[i].Type[:]) r.Actions[i].A = d.ReadUint64() @@ -425,11 +425,11 @@ func (r *RPCWriteRequest) DecodeFrom(d *types.Decoder) { } r.MerkleProof = d.ReadBool() r.RevisionNumber = d.ReadUint64() - r.ValidProofValues = make([]types.Currency, d.ReadPrefix()) + r.ValidProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.ValidProofValues { (*types.V1Currency)(&r.ValidProofValues[i]).DecodeFrom(d) } - r.MissedProofValues = make([]types.Currency, d.ReadPrefix()) + r.MissedProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.MissedProofValues { (*types.V1Currency)(&r.MissedProofValues[i]).DecodeFrom(d) } @@ -450,11 +450,11 @@ func (r *RPCWriteMerkleProof) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject. func (r *RPCWriteMerkleProof) DecodeFrom(d *types.Decoder) { - r.OldSubtreeHashes = make([]types.Hash256, d.ReadPrefix()) + r.OldSubtreeHashes = make([]types.Hash256, d.ReadPrefix(10000)) for i := range r.OldSubtreeHashes { d.Read(r.OldSubtreeHashes[i][:]) } - r.OldLeafHashes = make([]types.Hash256, d.ReadPrefix()) + r.OldLeafHashes = make([]types.Hash256, d.ReadPrefix(10000)) for i := range r.OldLeafHashes { d.Read(r.OldLeafHashes[i][:]) } diff --git a/rhp/v3/encoding.go b/rhp/v3/encoding.go index 33e2d29d..e6c67eed 100644 --- a/rhp/v3/encoding.go +++ b/rhp/v3/encoding.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" + rhp2 "go.sia.tech/core/rhp/v2" "go.sia.tech/core/types" ) @@ -154,11 +155,11 @@ func (r *PayByContractRequest) EncodeTo(e *types.Encoder) { func (r *PayByContractRequest) DecodeFrom(d *types.Decoder) { r.ContractID.DecodeFrom(d) r.RevisionNumber = d.ReadUint64() - r.ValidProofValues = make([]types.Currency, d.ReadPrefix()) + r.ValidProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.ValidProofValues { (*types.V1Currency)(&r.ValidProofValues[i]).DecodeFrom(d) } - r.MissedProofValues = make([]types.Currency, d.ReadPrefix()) + r.MissedProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.MissedProofValues { (*types.V1Currency)(&r.MissedProofValues[i]).DecodeFrom(d) } @@ -272,7 +273,7 @@ func (r *RPCExecuteProgramRequest) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject. func (r *RPCExecuteProgramRequest) DecodeFrom(d *types.Decoder) { r.FileContractID.DecodeFrom(d) - r.Program = make([]Instruction, d.ReadPrefix()) + r.Program = make([]Instruction, d.ReadPrefix(100)) for i := range r.Program { var id types.Specifier id.DecodeFrom(d) @@ -311,10 +312,10 @@ func (r *RPCExecuteProgramResponse) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject. func (r *RPCExecuteProgramResponse) DecodeFrom(d *types.Decoder) { (*types.V1Currency)(&r.AdditionalCollateral).DecodeFrom(d) - r.OutputLength = uint64(d.ReadPrefix()) + r.OutputLength = uint64(d.ReadPrefix(rhp2.SectorSize)) r.NewMerkleRoot.DecodeFrom(d) r.NewSize = d.ReadUint64() - r.Proof = make([]types.Hash256, d.ReadPrefix()) + r.Proof = make([]types.Hash256, d.ReadPrefix(10000)) for i := range r.Proof { r.Proof[i].DecodeFrom(d) } @@ -345,11 +346,11 @@ func (r *RPCFinalizeProgramRequest) EncodeTo(e *types.Encoder) { func (r *RPCFinalizeProgramRequest) DecodeFrom(d *types.Decoder) { copy(r.Signature[:], d.ReadBytes()) r.RevisionNumber = d.ReadUint64() - r.ValidProofValues = make([]types.Currency, d.ReadPrefix()) + r.ValidProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.ValidProofValues { (*types.V1Currency)(&r.ValidProofValues[i]).DecodeFrom(d) } - r.MissedProofValues = make([]types.Currency, d.ReadPrefix()) + r.MissedProofValues = make([]types.Currency, d.ReadPrefix(6)) for i := range r.MissedProofValues { (*types.V1Currency)(&r.MissedProofValues[i]).DecodeFrom(d) } @@ -397,7 +398,7 @@ func (r *RPCRenewContractRequest) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject func (r *RPCRenewContractRequest) DecodeFrom(d *types.Decoder) { - r.TransactionSet = make([]types.Transaction, d.ReadPrefix()) + r.TransactionSet = make([]types.Transaction, d.ReadPrefix(100)) for i := range r.TransactionSet { r.TransactionSet[i].DecodeFrom(d) } @@ -424,15 +425,15 @@ func (r *RPCRenewContractHostAdditions) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject func (r *RPCRenewContractHostAdditions) DecodeFrom(d *types.Decoder) { - r.Parents = make([]types.Transaction, d.ReadPrefix()) + r.Parents = make([]types.Transaction, d.ReadPrefix(100)) for i := range r.Parents { r.Parents[i].DecodeFrom(d) } - r.SiacoinInputs = make([]types.SiacoinInput, d.ReadPrefix()) + r.SiacoinInputs = make([]types.SiacoinInput, d.ReadPrefix(100)) for i := range r.SiacoinInputs { r.SiacoinInputs[i].DecodeFrom(d) } - r.SiacoinOutputs = make([]types.SiacoinOutput, d.ReadPrefix()) + r.SiacoinOutputs = make([]types.SiacoinOutput, d.ReadPrefix(100)) for i := range r.SiacoinOutputs { (*types.V1SiacoinOutput)(&r.SiacoinOutputs[i]).DecodeFrom(d) } @@ -450,7 +451,7 @@ func (r *RPCRenewSignatures) EncodeTo(e *types.Encoder) { // DecodeFrom implements ProtocolObject func (r *RPCRenewSignatures) DecodeFrom(d *types.Decoder) { - r.TransactionSignatures = make([]types.TransactionSignature, d.ReadPrefix()) + r.TransactionSignatures = make([]types.TransactionSignature, d.ReadPrefix(100)) for i := range r.TransactionSignatures { r.TransactionSignatures[i].DecodeFrom(d) } diff --git a/rhp/v3/transport.go b/rhp/v3/transport.go index c3a22428..560ca101 100644 --- a/rhp/v3/transport.go +++ b/rhp/v3/transport.go @@ -61,10 +61,10 @@ type Stream struct { readSub bool } -func (s *Stream) readObject(resp ProtocolObject, maxLen uint64) error { +func (s *Stream) readObject(resp ProtocolObject, maxLen int64) error { if s.readSub { d := types.NewDecoder(io.LimitedReader{R: s.s, N: minMessageSize}) - d.ReadPrefix() + d.ReadPrefix(4096) // subscriber name if errStr := d.ReadString(); errStr != "" { return errors.New(errStr) } else if d.Err() != nil { @@ -75,8 +75,10 @@ func (s *Stream) readObject(resp ProtocolObject, maxLen uint64) error { maxLen += minMessageSize // account for rpcResponse framing d := types.NewDecoder(io.LimitedReader{R: s.s, N: int64(maxLen)}) - if l := d.ReadPrefix(); uint64(l) > maxLen { + if l := d.ReadPrefix(maxLen); int64(l) > maxLen { return fmt.Errorf("message too long: %v > %v", l, maxLen) + } else if err := d.Err(); err != nil { + return fmt.Errorf("couldn't read message length: %w", err) } rr := rpcResponse{nil, resp} rr.DecodeFrom(d) @@ -159,7 +161,7 @@ func (s *Stream) ReadID() (rpcID types.Specifier, err error) { // read subscription and write response d := types.NewDecoder(io.LimitedReader{R: s.s, N: minMessageSize}) - d.ReadPrefix() + d.ReadPrefix(4096) // length-prefixed length-prefixed string sub := d.ReadString() if d.Err() != nil { return types.Specifier{}, d.Err() @@ -182,14 +184,14 @@ func (s *Stream) ReadID() (rpcID types.Specifier, err error) { } // ReadRequest reads an RPC request using the new loop protocol. -func (s *Stream) ReadRequest(req ProtocolObject, maxLen uint64) (err error) { +func (s *Stream) ReadRequest(req ProtocolObject, maxLen int64) (err error) { defer wrapErr(&err, "ReadRequest") return s.readObject(req, maxLen) } // ReadResponse reads an RPC response. If the response is an error, it is // returned directly. -func (s *Stream) ReadResponse(resp ProtocolObject, maxLen uint64) (err error) { +func (s *Stream) ReadResponse(resp ProtocolObject, maxLen int64) (err error) { defer wrapErr(&err, "ReadResponse") return s.readObject(resp, maxLen) } diff --git a/types/encoding.go b/types/encoding.go index ee281489..59d55169 100644 --- a/types/encoding.go +++ b/types/encoding.go @@ -10,6 +10,10 @@ import ( "time" ) +const ( + maxTransactionElements = 10_000 +) + // An Encoder writes Sia objects to an underlying stream. type Encoder struct { w io.Writer @@ -179,10 +183,10 @@ func (d *Decoder) ReadUint64() uint64 { // ReadPrefix reads a length prefix from the underlying stream. If the length // exceeds the number of bytes remaining in the stream, ReadPrefix sets d.Err // and returns 0. -func (d *Decoder) ReadPrefix() int { +func (d *Decoder) ReadPrefix(max int64) int { n := d.ReadUint64() - if n > uint64(d.lr.N) { - d.SetErr(fmt.Errorf("encoded object contains invalid length prefix (%v elems > %v bytes left in stream)", n, d.lr.N)) + if n > uint64(max) { + d.SetErr(fmt.Errorf("encoded object contains invalid length prefix (%v elems > %v elems)", n, max)) return 0 } return int(n) @@ -193,7 +197,7 @@ func (d *Decoder) ReadTime() time.Time { return time.Unix(int64(d.ReadUint64()), // ReadBytes reads a length-prefixed []byte from the underlying stream. func (d *Decoder) ReadBytes() []byte { - b := make([]byte, d.ReadPrefix()) + b := make([]byte, d.ReadPrefix(d.lr.N)) d.Read(b) return b } @@ -924,7 +928,7 @@ func (uk *UnlockKey) DecodeFrom(d *Decoder) { // DecodeFrom implements types.DecoderFrom. func (uc *UnlockConditions) DecodeFrom(d *Decoder) { uc.Timelock = d.ReadUint64() - uc.PublicKeys = make([]UnlockKey, d.ReadPrefix()) + uc.PublicKeys = make([]UnlockKey, d.ReadPrefix(maxTransactionElements)) for i := range uc.PublicKeys { uc.PublicKeys[i].DecodeFrom(d) } @@ -934,7 +938,7 @@ func (uc *UnlockConditions) DecodeFrom(d *Decoder) { // DecodeFrom implements types.DecoderFrom. func (c *V1Currency) DecodeFrom(d *Decoder) { var buf [16]byte - n := d.ReadPrefix() + n := d.ReadPrefix(16) if n > 16 { d.SetErr(fmt.Errorf("Currency too large: %v bytes", n)) return @@ -1013,11 +1017,11 @@ func (fc *FileContract) DecodeFrom(d *Decoder) { fc.WindowStart = d.ReadUint64() fc.WindowEnd = d.ReadUint64() (*V1Currency)(&fc.Payout).DecodeFrom(d) - fc.ValidProofOutputs = make([]SiacoinOutput, d.ReadPrefix()) + fc.ValidProofOutputs = make([]SiacoinOutput, d.ReadPrefix(maxTransactionElements)) for i := range fc.ValidProofOutputs { (*V1SiacoinOutput)(&fc.ValidProofOutputs[i]).DecodeFrom(d) } - fc.MissedProofOutputs = make([]SiacoinOutput, d.ReadPrefix()) + fc.MissedProofOutputs = make([]SiacoinOutput, d.ReadPrefix(maxTransactionElements)) for i := range fc.MissedProofOutputs { (*V1SiacoinOutput)(&fc.MissedProofOutputs[i]).DecodeFrom(d) } @@ -1037,11 +1041,11 @@ func (rev *FileContractRevision) DecodeFrom(d *Decoder) { rev.FileContract.FileMerkleRoot.DecodeFrom(d) rev.FileContract.WindowStart = d.ReadUint64() rev.FileContract.WindowEnd = d.ReadUint64() - rev.FileContract.ValidProofOutputs = make([]SiacoinOutput, d.ReadPrefix()) + rev.FileContract.ValidProofOutputs = make([]SiacoinOutput, d.ReadPrefix(maxTransactionElements)) for i := range rev.FileContract.ValidProofOutputs { (*V1SiacoinOutput)(&rev.FileContract.ValidProofOutputs[i]).DecodeFrom(d) } - rev.FileContract.MissedProofOutputs = make([]SiacoinOutput, d.ReadPrefix()) + rev.FileContract.MissedProofOutputs = make([]SiacoinOutput, d.ReadPrefix(maxTransactionElements)) for i := range rev.FileContract.MissedProofOutputs { (*V1SiacoinOutput)(&rev.FileContract.MissedProofOutputs[i]).DecodeFrom(d) } @@ -1055,7 +1059,7 @@ func (rev *FileContractRevision) DecodeFrom(d *Decoder) { func (sp *StorageProof) DecodeFrom(d *Decoder) { sp.ParentID.DecodeFrom(d) d.Read(sp.Leaf[:]) - sp.Proof = make([]Hash256, d.ReadPrefix()) + sp.Proof = make([]Hash256, d.ReadPrefix(maxTransactionElements)) for i := range sp.Proof { sp.Proof[i].DecodeFrom(d) } @@ -1082,7 +1086,7 @@ func (cf *CoveredFields) DecodeFrom(d *Decoder) { &cf.ArbitraryData, &cf.Signatures, } { - *f = make([]uint64, d.ReadPrefix()) + *f = make([]uint64, d.ReadPrefix(maxTransactionElements)) for i := range *f { (*f)[i] = d.ReadUint64() } @@ -1100,43 +1104,43 @@ func (ts *TransactionSignature) DecodeFrom(d *Decoder) { // DecodeFrom implements types.DecoderFrom. func (txn *Transaction) DecodeFrom(d *Decoder) { - txn.SiacoinInputs = make([]SiacoinInput, d.ReadPrefix()) + txn.SiacoinInputs = make([]SiacoinInput, d.ReadPrefix(maxTransactionElements)) for i := range txn.SiacoinInputs { txn.SiacoinInputs[i].DecodeFrom(d) } - txn.SiacoinOutputs = make([]SiacoinOutput, d.ReadPrefix()) + txn.SiacoinOutputs = make([]SiacoinOutput, d.ReadPrefix(maxTransactionElements)) for i := range txn.SiacoinOutputs { (*V1SiacoinOutput)(&txn.SiacoinOutputs[i]).DecodeFrom(d) } - txn.FileContracts = make([]FileContract, d.ReadPrefix()) + txn.FileContracts = make([]FileContract, d.ReadPrefix(maxTransactionElements)) for i := range txn.FileContracts { txn.FileContracts[i].DecodeFrom(d) } - txn.FileContractRevisions = make([]FileContractRevision, d.ReadPrefix()) + txn.FileContractRevisions = make([]FileContractRevision, d.ReadPrefix(maxTransactionElements)) for i := range txn.FileContractRevisions { txn.FileContractRevisions[i].DecodeFrom(d) } - txn.StorageProofs = make([]StorageProof, d.ReadPrefix()) + txn.StorageProofs = make([]StorageProof, d.ReadPrefix(maxTransactionElements)) for i := range txn.StorageProofs { txn.StorageProofs[i].DecodeFrom(d) } - txn.SiafundInputs = make([]SiafundInput, d.ReadPrefix()) + txn.SiafundInputs = make([]SiafundInput, d.ReadPrefix(maxTransactionElements)) for i := range txn.SiafundInputs { txn.SiafundInputs[i].DecodeFrom(d) } - txn.SiafundOutputs = make([]SiafundOutput, d.ReadPrefix()) + txn.SiafundOutputs = make([]SiafundOutput, d.ReadPrefix(maxTransactionElements)) for i := range txn.SiafundOutputs { (*V1SiafundOutput)(&txn.SiafundOutputs[i]).DecodeFrom(d) } - txn.MinerFees = make([]Currency, d.ReadPrefix()) + txn.MinerFees = make([]Currency, d.ReadPrefix(maxTransactionElements)) for i := range txn.MinerFees { (*V1Currency)(&txn.MinerFees[i]).DecodeFrom(d) } - txn.ArbitraryData = make([][]byte, d.ReadPrefix()) + txn.ArbitraryData = make([][]byte, d.ReadPrefix(maxTransactionElements)) for i := range txn.ArbitraryData { txn.ArbitraryData[i] = d.ReadBytes() } - txn.Signatures = make([]TransactionSignature, d.ReadPrefix()) + txn.Signatures = make([]TransactionSignature, d.ReadPrefix(maxTransactionElements)) for i := range txn.Signatures { txn.Signatures[i].DecodeFrom(d) } @@ -1247,7 +1251,7 @@ func (sp *SatisfiedPolicy) DecodeFrom(d *Decoder) { func (se *StateElement) DecodeFrom(d *Decoder) { se.ID.DecodeFrom(d) se.LeafIndex = d.ReadUint64() - se.MerkleProof = make([]Hash256, d.ReadPrefix()) + se.MerkleProof = make([]Hash256, d.ReadPrefix(maxTransactionElements)) for i := range se.MerkleProof { se.MerkleProof[i].DecodeFrom(d) } @@ -1340,7 +1344,7 @@ func (fcf V2FileContractFinalization) DecodeFrom(d *Decoder) { func (sp *V2StorageProof) DecodeFrom(d *Decoder) { sp.ProofIndex.DecodeFrom(d) d.Read(sp.Leaf[:]) - sp.Proof = make([]Hash256, d.ReadPrefix()) + sp.Proof = make([]Hash256, d.ReadPrefix(maxTransactionElements)) for i := range sp.Proof { sp.Proof[i].DecodeFrom(d) } @@ -1385,49 +1389,49 @@ func (txn *V2Transaction) DecodeFrom(d *Decoder) { fields := d.ReadUint64() if fields&(1<<0) != 0 { - txn.SiacoinInputs = make([]V2SiacoinInput, d.ReadPrefix()) + txn.SiacoinInputs = make([]V2SiacoinInput, d.ReadPrefix(maxTransactionElements)) for i := range txn.SiacoinInputs { txn.SiacoinInputs[i].DecodeFrom(d) } } if fields&(1<<1) != 0 { - txn.SiacoinOutputs = make([]SiacoinOutput, d.ReadPrefix()) + txn.SiacoinOutputs = make([]SiacoinOutput, d.ReadPrefix(maxTransactionElements)) for i := range txn.SiacoinOutputs { (*V2SiacoinOutput)(&txn.SiacoinOutputs[i]).DecodeFrom(d) } } if fields&(1<<2) != 0 { - txn.SiafundInputs = make([]V2SiafundInput, d.ReadPrefix()) + txn.SiafundInputs = make([]V2SiafundInput, d.ReadPrefix(maxTransactionElements)) for i := range txn.SiafundInputs { txn.SiafundInputs[i].DecodeFrom(d) } } if fields&(1<<3) != 0 { - txn.SiafundOutputs = make([]SiafundOutput, d.ReadPrefix()) + txn.SiafundOutputs = make([]SiafundOutput, d.ReadPrefix(maxTransactionElements)) for i := range txn.SiafundOutputs { (*V2SiafundOutput)(&txn.SiafundOutputs[i]).DecodeFrom(d) } } if fields&(1<<4) != 0 { - txn.FileContracts = make([]V2FileContract, d.ReadPrefix()) + txn.FileContracts = make([]V2FileContract, d.ReadPrefix(maxTransactionElements)) for i := range txn.FileContracts { txn.FileContracts[i].DecodeFrom(d) } } if fields&(1<<5) != 0 { - txn.FileContractRevisions = make([]V2FileContractRevision, d.ReadPrefix()) + txn.FileContractRevisions = make([]V2FileContractRevision, d.ReadPrefix(maxTransactionElements)) for i := range txn.FileContractRevisions { txn.FileContractRevisions[i].DecodeFrom(d) } } if fields&(1<<6) != 0 { - txn.FileContractResolutions = make([]V2FileContractResolution, d.ReadPrefix()) + txn.FileContractResolutions = make([]V2FileContractResolution, d.ReadPrefix(maxTransactionElements)) for i := range txn.FileContractResolutions { txn.FileContractResolutions[i].DecodeFrom(d) } } if fields&(1<<7) != 0 { - txn.Attestations = make([]Attestation, d.ReadPrefix()) + txn.Attestations = make([]Attestation, d.ReadPrefix(maxTransactionElements)) for i := range txn.Attestations { txn.Attestations[i].DecodeFrom(d) } @@ -1456,11 +1460,11 @@ func (b *V1Block) DecodeFrom(d *Decoder) { b.ParentID.DecodeFrom(d) b.Nonce = d.ReadUint64() b.Timestamp = d.ReadTime() - b.MinerPayouts = make([]SiacoinOutput, d.ReadPrefix()) + b.MinerPayouts = make([]SiacoinOutput, d.ReadPrefix(maxTransactionElements)) for i := range b.MinerPayouts { (*V1SiacoinOutput)(&b.MinerPayouts[i]).DecodeFrom(d) } - b.Transactions = make([]Transaction, d.ReadPrefix()) + b.Transactions = make([]Transaction, d.ReadPrefix(maxTransactionElements)) for i := range b.Transactions { b.Transactions[i].DecodeFrom(d) } diff --git a/types/encoding_test.go b/types/encoding_test.go index 1f0d8638..c63b9e03 100644 --- a/types/encoding_test.go +++ b/types/encoding_test.go @@ -30,7 +30,7 @@ func TestDecoderError(t *testing.T) { }() // read the value from the encoder - n := d.ReadPrefix() + n := d.ReadPrefix(1000) if n != 1000 { t.Fatalf("expected 1000, got %d", n) } @@ -39,8 +39,28 @@ func TestDecoderError(t *testing.T) { re.err = io.EOF // should return 0 since the decoder errored - n = d.ReadPrefix() + n = d.ReadPrefix(1000) if n != 0 { t.Fatalf("expected 0, got %d", n) } } + +func TestPrefixLimit(t *testing.T) { + r, w := io.Pipe() + re := &readErrorer{r: r} + enc := NewEncoder(w) + d := NewDecoder(io.LimitedReader{R: re, N: 2000}) + + go func() { + // writing to the pipe blocks until we read from it + enc.WritePrefix(1001) + enc.Flush() + }() + + n := d.ReadPrefix(1000) + if n != 0 { + t.Fatalf("expected 0, got %d", n) + } else if err := d.Err(); err == nil { + t.Fatal("expected decoding error") + } +} diff --git a/types/multiproof.go b/types/multiproof.go index e6c55938..9fc6a189 100644 --- a/types/multiproof.go +++ b/types/multiproof.go @@ -210,7 +210,7 @@ func (txns V2TransactionsMultiproof) EncodeTo(e *Encoder) { // DecodeFrom implements types.DecoderFrom. func (txns *V2TransactionsMultiproof) DecodeFrom(d *Decoder) { - *txns = make(V2TransactionsMultiproof, d.ReadPrefix()) + *txns = make(V2TransactionsMultiproof, d.ReadPrefix(maxTransactionElements)) for i := range *txns { (*txns)[i].DecodeFrom(d) }