From b03419ee92fdb84f23acfbd492b3fc2ae7b1282b Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Sat, 19 Sep 2026 23:21:33 -0700 Subject: [PATCH] s3: reject UploadPart checksum algorithms conflicting with the upload An UploadPart that explicitly selects a different checksum algorithm than the one declared at CreateMultipartUpload would store a checksum CompleteMultipartUpload could never accept. Reject the conflict up front with InvalidRequest, matching AWS. --- test/s3/checksum/issue11401_repro_test.go | 30 +++++++++++++++++++ weed/s3api/s3api_object_handlers_multipart.go | 13 +++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/test/s3/checksum/issue11401_repro_test.go b/test/s3/checksum/issue11401_repro_test.go index 3aee839c3..00c6ce379 100644 --- a/test/s3/checksum/issue11401_repro_test.go +++ b/test/s3/checksum/issue11401_repro_test.go @@ -211,3 +211,33 @@ func TestCompleteMultipartUploadValidatesPartChecksums(t *testing.T) { require.NoError(t, complete(part.ChecksumSHA256)) } + +// UploadPart with a checksum algorithm that conflicts with the one declared at +// CreateMultipartUpload is rejected, as on AWS. +func TestMultipartPartConflictingAlgorithm(t *testing.T) { + client := newWhenRequiredChecksumClient(t) + + bucket := uniqueBucket() + createBucket(t, client, bucket) + defer cleanupBucket(t, client, bucket) + + create, err := client.CreateMultipartUpload(context.Background(), &s3.CreateMultipartUploadInput{ + Bucket: aws.String(bucket), + Key: aws.String("conflict"), + ChecksumAlgorithm: types.ChecksumAlgorithmSha256, + }) + require.NoError(t, err) + + _, err = client.UploadPart(context.Background(), &s3.UploadPartInput{ + Bucket: aws.String(bucket), + Key: aws.String("conflict"), + UploadId: create.UploadId, + PartNumber: aws.Int32(1), + Body: bytes.NewReader([]byte("data")), + ChecksumAlgorithm: types.ChecksumAlgorithmCrc32, + }) + var apiErr smithy.APIError + require.Error(t, err) + require.True(t, errors.As(err, &apiErr)) + require.Equal(t, "InvalidRequest", apiErr.ErrorCode()) +} diff --git a/weed/s3api/s3api_object_handlers_multipart.go b/weed/s3api/s3api_object_handlers_multipart.go index 367e87706..31728847f 100644 --- a/weed/s3api/s3api_object_handlers_multipart.go +++ b/weed/s3api/s3api_object_handlers_multipart.go @@ -446,11 +446,16 @@ func (s3a *S3ApiServer) PutObjectPartHandler(w http.ResponseWriter, r *http.Requ } // Parts inherit the checksum algorithm declared at CreateMultipartUpload - // when the request doesn't specify one (AWS behavior). + // when the request doesn't specify one; a conflicting one is rejected. if headerName := string(uploadEntry.Extended[s3_constants.ExtChecksumAlgorithm]); headerName != "" { - if algo, _, code := detectRequestedChecksumAlgorithm(r); code == s3err.ErrNone && algo == ChecksumAlgorithmNone { - if name := checksumAlgorithmNameFromHeaderName(headerName); name != "" { - r.Header.Set(s3_constants.AmzChecksumAlgorithm, name) + if algo, reqHeaderName, code := detectRequestedChecksumAlgorithm(r); code == s3err.ErrNone { + if algo == ChecksumAlgorithmNone { + if name := checksumAlgorithmNameFromHeaderName(headerName); name != "" { + r.Header.Set(s3_constants.AmzChecksumAlgorithm, name) + } + } else if reqHeaderName != headerName { + s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest) + return } } }