diff --git a/CHANGELOG.md b/CHANGELOG.md index a4cbaeb812..6ccf7df96d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Changelog for NeoFS Node - Setting eACL without API version in CLI (#4229) - Unnecessary local shard relocation of EC parts by policer (#4236) - GC stalling on expired objects protected by LOCKs stored only on other shards (#4237) +- Unchecked known payload length of unfinished objects in PUT (#4233) ### Changed - SN now requires requests to have API version >= 2.22 (#4212) diff --git a/pkg/services/object/server.go b/pkg/services/object/server.go index ba9cec44db..dd60bc66b0 100644 --- a/pkg/services/object/server.go +++ b/pkg/services/object/server.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "io" + "math" "slices" "strings" "sync" @@ -309,7 +310,8 @@ type putStream struct { initReq *protoobject.PutRequest chunkReqs []*protoobject.PutRequest - expBytes, recvBytes uint64 // payload + knownPayloadLength bool + expBytes, recvBytes uint64 // payload (undefined if knownPayloadLength is false) } func newIntermediatePutStream(signer ecdsa.PrivateKey, base *putsvc.Streamer, ctx context.Context, log *zap.Logger) *putStream { @@ -378,11 +380,24 @@ func (x *putStream) forwardInitRequest(req *protoobject.PutRequest, initPart *pr return fmt.Errorf("could not init object put stream: %w", err) } + ln := initPart.Header.GetPayloadLength() + + // Max is a special value defined in the protocol. Zero is also treated as + // unknown because server never prohibited such requests, but clients often + // forgot to explicitly set the value. + x.knownPayloadLength = ln > 0 && ln != math.MaxUint64 + if x.knownPayloadLength { + x.expBytes = ln + } + if x.cacheReqs = initPart.Signature != nil; !x.cacheReqs { return nil } - x.expBytes = initPart.Header.GetPayloadLength() + if ln == math.MaxUint64 { + return putsvc.ErrExceedingMaxSize + } + if m := x.base.MaxObjectSize(); x.expBytes > m { return putsvc.ErrExceedingMaxSize } @@ -391,7 +406,7 @@ func (x *putStream) forwardInitRequest(req *protoobject.PutRequest, initPart *pr } func (x *putStream) forwardChunkRequest(req *protoobject.PutRequest, c []byte) error { - if x.cacheReqs { + if x.knownPayloadLength { if x.recvBytes += uint64(len(c)); x.recvBytes > x.expBytes { return putsvc.ErrWrongPayloadSize } @@ -407,7 +422,7 @@ func (x *putStream) forwardChunkRequest(req *protoobject.PutRequest, c []byte) e } func (x *putStream) close() (*protoobject.PutResponse, error) { - if x.cacheReqs && x.recvBytes != x.expBytes { + if x.knownPayloadLength && x.recvBytes != x.expBytes { return nil, putsvc.ErrWrongPayloadSize }