Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changelog/bugfix-pr3944.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "bugfix",
"category": "aws-cpp-sdk-s3-crt",
"contributor": "FranciscoMaxwell",
"description": "Fix use-after-free in S3 CRT async operations by owning a request copy"
}
Original file line number Diff line number Diff line change
Expand Up @@ -8739,7 +8739,7 @@ class AWS_S3CRT_API S3CrtClient : public Aws::Client::AWSXMLClient,
PutObjectResponseReceivedHandler putResponseHandler;
CopyObjectResponseReceivedHandler copyResponseHandler;
std::shared_ptr<const Aws::Client::AsyncCallerContext> asyncCallerContext;
const Aws::AmazonWebServiceRequest* originalRequest;
Aws::UniquePtr<Aws::AmazonWebServiceRequest> originalRequest;
std::shared_ptr<Aws::Http::HttpRequest> request;
std::shared_ptr<Aws::Http::HttpResponse> response;
std::shared_ptr<Aws::Crt::Http::HttpRequest> crtHttpRequest;
Expand Down Expand Up @@ -8778,7 +8778,7 @@ class AWS_S3CRT_API S3CrtClient : public Aws::Client::AWSXMLClient,
void InitCrtEndpointFromUri(aws_uri& endpoint_uri, const Aws::Http::URI& uri) const;

void InitCommonCrtRequestOption(CrtRequestCallbackUserData* userData, aws_s3_meta_request_options* options,
const Aws::AmazonWebServiceRequest* request, const Aws::Http::URI& uri,
Aws::UniquePtr<Aws::AmazonWebServiceRequest> request, const Aws::Http::URI& uri,
Aws::Http::HttpMethod method) const;

using CopyObjectPropertiesOutcome = Aws::Utils::Outcome<Aws::Map<Aws::String, Aws::String>, S3CrtError>;
Expand Down
22 changes: 14 additions & 8 deletions generated/src/aws-cpp-sdk-s3-crt/source/S3CrtClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ void S3CrtClient::InitCrtEndpointFromUri(aws_uri& endpoint_uri, const Aws::Http:
}

void S3CrtClient::InitCommonCrtRequestOption(CrtRequestCallbackUserData* userData, aws_s3_meta_request_options* options,
const Aws::AmazonWebServiceRequest* request, const Aws::Http::URI& uri,
Aws::UniquePtr<Aws::AmazonWebServiceRequest> request, const Aws::Http::URI& uri,
Aws::Http::HttpMethod method) const {
std::shared_ptr<HttpRequest> httpRequest(nullptr);
if (request) {
Expand All @@ -812,7 +812,7 @@ void S3CrtClient::InitCommonCrtRequestOption(CrtRequestCallbackUserData* userDat
std::shared_ptr<HttpResponse> httpResponse = Aws::MakeShared<Aws::Http::Standard::StandardHttpResponse>(ALLOCATION_TAG, httpRequest);

userData->s3CrtClient = this;
userData->originalRequest = request;
userData->originalRequest = std::move(request);
userData->request = httpRequest;
userData->response = httpResponse;

Expand Down Expand Up @@ -1000,7 +1000,7 @@ static void CopyObjectRequestShutdownCallback(void* user_data) {
userData->asyncCallerContext->GetMonitorContext().OnRequestSucceeded(userData->request, userData->response);
}
}
userData->copyResponseHandler(userData->s3CrtClient, *(reinterpret_cast<const CopyObjectRequest*>(userData->originalRequest)),
userData->copyResponseHandler(userData->s3CrtClient, *(static_cast<const CopyObjectRequest*>(userData->originalRequest.get())),
std::move(outcome), userData->asyncCallerContext);

Aws::Delete(userData);
Expand Down Expand Up @@ -1071,7 +1071,9 @@ void S3CrtClient::CopyObjectAsync(const CopyObjectRequest& request, const CopyOb
if (handlerContext) {
userData->asyncCallerContext = handlerContext;
}
InitCommonCrtRequestOption(userData, &options, &request, endpointResolutionOutcome.GetResult().GetURI(), Aws::Http::HttpMethod::HTTP_PUT);
auto requestCopy = Aws::MakeUnique<CopyObjectRequest>(ALLOCATION_TAG, request);
InitCommonCrtRequestOption(userData, &options, std::move(requestCopy), endpointResolutionOutcome.GetResult().GetURI(),
Aws::Http::HttpMethod::HTTP_PUT);
if (userData != nullptr && userData->request != nullptr && userData->request->GetContentBody() != nullptr &&
userData->request->GetContentBody()->fail()) {
return handler(this, request,
Expand Down Expand Up @@ -1245,7 +1247,7 @@ static void GetObjectRequestShutdownCallback(void* user_data) {
userData->asyncCallerContext->GetMonitorContext().OnRequestSucceeded(userData->request, userData->response);
}
}
userData->getResponseHandler(userData->s3CrtClient, *(reinterpret_cast<const GetObjectRequest*>(userData->originalRequest)),
userData->getResponseHandler(userData->s3CrtClient, *(static_cast<const GetObjectRequest*>(userData->originalRequest.get())),
std::move(outcome), userData->asyncCallerContext);

Aws::Delete(userData);
Expand Down Expand Up @@ -1309,7 +1311,9 @@ void S3CrtClient::GetObjectAsync(const GetObjectRequest& request, const GetObjec
if (handlerContext) {
userData->asyncCallerContext = handlerContext;
}
InitCommonCrtRequestOption(userData, &options, &request, endpointResolutionOutcome.GetResult().GetURI(), Aws::Http::HttpMethod::HTTP_GET);
auto requestCopy = Aws::MakeUnique<GetObjectRequest>(ALLOCATION_TAG, request);
InitCommonCrtRequestOption(userData, &options, std::move(requestCopy), endpointResolutionOutcome.GetResult().GetURI(),
Aws::Http::HttpMethod::HTTP_GET);
if (userData != nullptr && userData->request != nullptr && userData->request->GetContentBody() != nullptr &&
userData->request->GetContentBody()->fail()) {
return handler(this, request,
Expand Down Expand Up @@ -1406,7 +1410,7 @@ static void PutObjectRequestShutdownCallback(void* user_data) {
userData->asyncCallerContext->GetMonitorContext().OnRequestSucceeded(userData->request, userData->response);
}
}
userData->putResponseHandler(userData->s3CrtClient, *(reinterpret_cast<const PutObjectRequest*>(userData->originalRequest)),
userData->putResponseHandler(userData->s3CrtClient, *(static_cast<const PutObjectRequest*>(userData->originalRequest.get())),
std::move(outcome), userData->asyncCallerContext);

Aws::Delete(userData);
Expand Down Expand Up @@ -1470,7 +1474,9 @@ void S3CrtClient::PutObjectAsync(const PutObjectRequest& request, const PutObjec
if (handlerContext) {
userData->asyncCallerContext = handlerContext;
}
InitCommonCrtRequestOption(userData, &options, &request, endpointResolutionOutcome.GetResult().GetURI(), Aws::Http::HttpMethod::HTTP_PUT);
auto requestCopy = Aws::MakeUnique<PutObjectRequest>(ALLOCATION_TAG, request);
InitCommonCrtRequestOption(userData, &options, std::move(requestCopy), endpointResolutionOutcome.GetResult().GetURI(),
Aws::Http::HttpMethod::HTTP_PUT);
if (userData != nullptr && userData->request != nullptr && userData->request->GetContentBody() != nullptr &&
userData->request->GetContentBody()->fail()) {
return handler(this, request,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include <aws/testing/platform/PlatformTesting.h>
#include <aws/testing/TestingEnvironment.h>
#include <fstream>
#include <future>

#ifdef _WIN32
#pragma warning(disable: 4127)
Expand Down Expand Up @@ -1533,6 +1534,70 @@ namespace
AWS_ASSERT_SUCCESS(putObjectOutcome);
}

TEST_F(BucketAndObjectOperationTest, TestAsyncRequestsOutliveCallerRequest)
{
const Aws::String fullBucketName = CalculateBucketName(BASE_PUT_OBJECTS_BUCKET_NAME.c_str());
SCOPED_TRACE(Aws::String("FullBucketName ") + fullBucketName);
CreateBucketRequest createBucketRequest;
createBucketRequest.SetBucket(fullBucketName);
createBucketRequest.SetACL(BucketCannedACL::private_);

CreateBucketOutcome createBucketOutcome = Client->CreateBucket(createBucketRequest);
AWS_ASSERT_SUCCESS(createBucketOutcome);
ASSERT_TRUE(WaitForBucketToPropagate(fullBucketName));
TagTestBucket(fullBucketName, Client);

const Aws::String key = "async-lifetime";
const Aws::String copyKey = "async-lifetime-copy";

std::promise<std::pair<Aws::String, PutObjectOutcome>> putPromise;
{
PutObjectRequest putObjectRequest;
putObjectRequest.SetBucket(fullBucketName);
putObjectRequest.SetKey(key);
putObjectRequest.SetBody(Aws::MakeShared<StringStream>(ALLOCATION_TAG, "Test Object"));
Client->PutObjectAsync(putObjectRequest,
[&putPromise](const S3CrtClient*, const PutObjectRequest& request, const PutObjectOutcome& outcome,
const std::shared_ptr<const Aws::Client::AsyncCallerContext>&) {
putPromise.set_value({request.GetKey(), outcome});
});
}
auto putResult = putPromise.get_future().get();
AWS_ASSERT_SUCCESS(putResult.second);
EXPECT_EQ(key, putResult.first);

std::promise<std::pair<Aws::String, bool>> getPromise;
{
GetObjectRequest getObjectRequest;
getObjectRequest.SetBucket(fullBucketName);
getObjectRequest.SetKey(key);
Client->GetObjectAsync(getObjectRequest,
[&getPromise](const S3CrtClient*, const GetObjectRequest& request, GetObjectOutcome outcome,
const std::shared_ptr<const Aws::Client::AsyncCallerContext>&) {
getPromise.set_value({request.GetKey(), outcome.IsSuccess()});
});
}
auto getResult = getPromise.get_future().get();
EXPECT_TRUE(getResult.second);
EXPECT_EQ(key, getResult.first);

std::promise<std::pair<Aws::String, CopyObjectOutcome>> copyPromise;
{
CopyObjectRequest copyObjectRequest;
copyObjectRequest.SetBucket(fullBucketName);
copyObjectRequest.SetKey(copyKey);
copyObjectRequest.SetCopySource(fullBucketName + "/" + key);
Client->CopyObjectAsync(copyObjectRequest,
[&copyPromise](const S3CrtClient*, const CopyObjectRequest& request, const CopyObjectOutcome& outcome,
const std::shared_ptr<const Aws::Client::AsyncCallerContext>&) {
copyPromise.set_value({request.GetKey(), outcome});
});
}
auto copyResult = copyPromise.get_future().get();
AWS_ASSERT_SUCCESS(copyResult.second);
EXPECT_EQ(copyKey, copyResult.first);
}

TEST_F(BucketAndObjectOperationTest, NoAuthPublicBucket) {
Aws::S3Crt::ClientConfiguration s3ClientConfig;
s3ClientConfig.region = Aws::Region::US_EAST_1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ namespace ${rootNamespace}
PutObjectResponseReceivedHandler putResponseHandler;
CopyObjectResponseReceivedHandler copyResponseHandler;
std::shared_ptr<const Aws::Client::AsyncCallerContext> asyncCallerContext;
const Aws::AmazonWebServiceRequest *originalRequest;
Aws::UniquePtr<Aws::AmazonWebServiceRequest> originalRequest;
std::shared_ptr<Aws::Http::HttpRequest> request;
std::shared_ptr<Aws::Http::HttpResponse> response;
std::shared_ptr<Aws::Crt::Http::HttpRequest> crtHttpRequest;
Expand Down Expand Up @@ -252,7 +252,7 @@ namespace ${rootNamespace}

void InitCommonCrtRequestOption(CrtRequestCallbackUserData *userData,
aws_s3_meta_request_options *options,
const Aws::AmazonWebServiceRequest *request,
Aws::UniquePtr<Aws::AmazonWebServiceRequest> request,
const Aws::Http::URI &uri, Aws::Http::HttpMethod method) const;

using CopyObjectPropertiesOutcome = Aws::Utils::Outcome<Aws::Map<Aws::String, Aws::String>, S3CrtError>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ namespace ${rootNamespace}
PutObjectResponseReceivedHandler putResponseHandler;
CopyObjectResponseReceivedHandler copyResponseHandler;
std::shared_ptr<const Aws::Client::AsyncCallerContext> asyncCallerContext;
const Aws::AmazonWebServiceRequest *originalRequest;
Aws::UniquePtr<Aws::AmazonWebServiceRequest> originalRequest;
std::shared_ptr<Aws::Http::HttpRequest> request;
std::shared_ptr<Aws::Http::HttpResponse> response;
std::shared_ptr<Aws::Crt::Http::HttpRequest> crtHttpRequest;
Expand Down Expand Up @@ -264,7 +264,7 @@ namespace ${rootNamespace}

void InitCommonCrtRequestOption(CrtRequestCallbackUserData *userData,
aws_s3_meta_request_options *options,
const Aws::AmazonWebServiceRequest *request,
Aws::UniquePtr<Aws::AmazonWebServiceRequest> request,
const Aws::Http::URI &uri, Aws::Http::HttpMethod method) const;

using CopyObjectPropertiesOutcome = Aws::Utils::Outcome<Aws::Map<Aws::String, Aws::String>, S3CrtError>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ void S3CrtClient::InitCrtEndpointFromUri(aws_uri &endpoint_uri, const Aws::Http:

void S3CrtClient::InitCommonCrtRequestOption(CrtRequestCallbackUserData *userData,
aws_s3_meta_request_options *options,
const Aws::AmazonWebServiceRequest *request,
Aws::UniquePtr<Aws::AmazonWebServiceRequest> request,
const Aws::Http::URI &uri,
Aws::Http::HttpMethod method) const
{
Expand All @@ -295,7 +295,7 @@ void S3CrtClient::InitCommonCrtRequestOption(CrtRequestCallbackUserData *userDat
std::shared_ptr<HttpResponse> httpResponse = Aws::MakeShared<Aws::Http::Standard::StandardHttpResponse>(ALLOCATION_TAG, httpRequest);

userData->s3CrtClient = this;
userData->originalRequest = request;
userData->originalRequest = std::move(request);
userData->request = httpRequest;
userData->response = httpResponse;

Expand Down Expand Up @@ -512,11 +512,11 @@ static void ${operation.name}RequestShutdownCallback(void *user_data)
}
#if($operation.request)
#if($operation.name == "PutObject")
userData->putResponseHandler(userData->s3CrtClient, *(reinterpret_cast<const ${operation.request.shape.name}*>(userData->originalRequest)), std::move(outcome), userData->asyncCallerContext);
userData->putResponseHandler(userData->s3CrtClient, *(static_cast<const ${operation.request.shape.name}*>(userData->originalRequest.get())), std::move(outcome), userData->asyncCallerContext);
#elseif($operation.name == "GetObject")
userData->getResponseHandler(userData->s3CrtClient, *(reinterpret_cast<const ${operation.request.shape.name}*>(userData->originalRequest)), std::move(outcome), userData->asyncCallerContext);
userData->getResponseHandler(userData->s3CrtClient, *(static_cast<const ${operation.request.shape.name}*>(userData->originalRequest.get())), std::move(outcome), userData->asyncCallerContext);
#elseif($operation.name == "CopyObject")
userData->copyResponseHandler(userData->s3CrtClient, *(reinterpret_cast<const ${operation.request.shape.name}*>(userData->originalRequest)), std::move(outcome), userData->asyncCallerContext);
userData->copyResponseHandler(userData->s3CrtClient, *(static_cast<const ${operation.request.shape.name}*>(userData->originalRequest.get())), std::move(outcome), userData->asyncCallerContext);
#end
#else
(*handler)(userData->s3CrtClient, outcome, userData->userCallbackContext);
Expand Down Expand Up @@ -594,7 +594,8 @@ void ${className}::${operation.name}Async(${constText}${operation.request.shape.
userData->asyncCallerContext = handlerContext;
}
#if($serviceModel.endpointRules)
InitCommonCrtRequestOption(userData, &options, &request, endpointResolutionOutcome.GetResult().GetURI(), Aws::Http::HttpMethod::HTTP_${operation.http.method});
auto requestCopy = Aws::MakeUnique<${operation.request.shape.name}>(ALLOCATION_TAG, request);
InitCommonCrtRequestOption(userData, &options, std::move(requestCopy), endpointResolutionOutcome.GetResult().GetURI(), Aws::Http::HttpMethod::HTTP_${operation.http.method});
if (userData != nullptr &&
userData->request != nullptr &&
userData->request->GetContentBody() != nullptr &&
Expand All @@ -609,7 +610,8 @@ void ${className}::${operation.name}Async(${constText}${operation.request.shape.
#end
}
#else
InitCommonCrtRequestOption(userData, &options, &request, uri, Aws::Http::HttpMethod::HTTP_${operation.http.method});
auto requestCopy = Aws::MakeUnique<${operation.request.shape.name}>(ALLOCATION_TAG, request);
InitCommonCrtRequestOption(userData, &options, std::move(requestCopy), uri, Aws::Http::HttpMethod::HTTP_${operation.http.method});
#end
#if($operation.name == "CopyObject")
{
Expand Down
Loading
Loading