Skip to content

Segmentation fault in Aws::S3::S3Client::GetObject #781

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
oliora opened this issue Jan 22, 2018 · 4 comments
Closed

Segmentation fault in Aws::S3::S3Client::GetObject #781

oliora opened this issue Jan 22, 2018 · 4 comments
Labels
bug This issue is a bug.

Comments

@oliora
Copy link
Contributor

oliora commented Jan 22, 2018

We build SDK of version 47030e9 with GCC 4.9.4 and run it on Linux @ x86_64, although the issue should occur with any platform/OS/compiler.

Segfault Call stack:

Thu Jan 18 12:38:16 2018: [0x3501701C47A11A44] [FATAL  ] Caught SIGSEGV (11), traceback:
Thu Jan 18 12:38:16 2018: [0x3501701C47A11A44] [ERROR  ] 00000000005932c1 <our-server-executable>(sigsegv(int)+0xd1) [0x5932c1]
Thu Jan 18 12:38:16 2018: [0x3501701C47A11A44] [ERROR  ] 00007ff0652ee7e0 /lib64/libpthread.so.0(+0xf7e0) [0x7ff0652ee7e0]
Thu Jan 18 12:38:16 2018: [0x3501701C47A11A44] [ERROR  ] 00007ff06407a624 libaws-cpp-sdk-core.so(Aws::External::tinyxml2::StrPair::GetStr()+0x14) [0x7ff06407a624]
Thu Jan 18 12:38:16 2018: [0x3501701C47A11A44] [ERROR  ] 00007ff0640bbbb4 libaws-cpp-sdk-core.so(Aws::Utils::Xml::XmlNode::GetName() const+0x14) [0x7ff0640bbbb4]
Thu Jan 18 12:38:16 2018: [0x3501701C47A11A44] [ERROR  ] 00007ff0640a0f5e libaws-cpp-sdk-core.so(Aws::Client::XmlErrorMarshaller::ContainsError(Aws::Http::HttpResponse const&)+0x8e) [0x7ff0640a0f5e]
Thu Jan 18 12:38:16 2018: [0x3501701C47A11A44] [ERROR  ] 00007ff0640969d9 libaws-cpp-sdk-core.so(Aws::Client::AWSClient::AttemptOneRequest(Aws::Http::URI const&, Aws::AmazonWebServiceRequest const&, Aws::Http::HttpMethod, char const*) const+0x369) [0x7ff0640969d9]
Thu Jan 18 12:38:16 2018: [0x3501701C47A11A44] [ERROR  ] 00007ff064099543 libaws-cpp-sdk-core.so(Aws::Client::AWSClient::AttemptExhaustively(Aws::Http::URI const&, Aws::AmazonWebServiceRequest const&, Aws::Http::HttpMethod, char const*) const+0x493) [0x7ff064099543]
Thu Jan 18 12:38:16 2018: [0x3501701C47A11A44] [ERROR  ] 00007ff06409b7f6 libaws-cpp-sdk-core.so(Aws::Client::AWSClient::MakeRequestWithUnparsedResponse(Aws::Http::URI const&, Aws::AmazonWebServiceRequest const&, Aws::Http::HttpMethod, char const*) const+0x26) [0x7ff06409b7f6]
Thu Jan 18 12:38:16 2018: [0x3501701C47A11A44] [ERROR  ] 00007ff063f094f7 libaws-cpp-sdk-s3.so(Aws::S3::S3Client::GetObject(Aws::S3::Model::GetObjectRequest const&) const+0x137) [0x7ff063f094f7]

The crash is happening in TinyXML2 because of the old bug in parsing malformed entities leethomason/tinyxml2#291

I'm quite sure that recent change f5c0f79 had exposed the TinyXML2 bug.
In short, this change tries to parse every S3 response body as XML document to extract an error from it. S3 response body is customer’s S3 object thus just some (random) bytes. Parsing every S3 object as XML effectively means performing a fuzzy testing of TinyXML2 which is apparently failed :-)

The possible solution for the crash would be to update TinyXML2 version used in the SDK. I've created a separate ticket for this (since it need to be done regardless of this issue): #777

In addition to the TinyXML2 fix I’m also concerned of the following:

  1. What is performance impact of parsing every S3 object loaded from S3? Can’t you scope down the response body parsing to only parse CompleteMultipartUpload responses since they’re only ones that can return 200 OK + error in the body? Extra option would be to parse only responses that are smaller than a certain size (128K, 1M?) because if it’s bigger it’s definitely not an error but customer’s S3 object.
  2. Parsing of every downloaded S3 object seems error-prone. What if customer’s S3 object is an XML with “Error” root element? One can’t be downloaded at all because it’s interpreted by the SDK as request processing error rather than as actual customer’s document. Scoping down response parsing to only CompleteMultipartUpload responses would fix the problem as well.

For me it seems that scoping down response parsing to CompleteMultipartUpload responses is the only real solution for the issue.

@oliora
Copy link
Contributor Author

oliora commented Jan 22, 2018

I can reproduce both crash and weird behavior when customer's S3 object has <Error> tag with this code:

#include <aws/s3/S3Client.h>
#include <aws/s3/model/GetObjectRequest.h>
#include <aws/core/Aws.h>

int main(int argc, char *argv[])
{
    Aws::SDKOptions options;
    Aws::InitAPI(options);

    Aws::Client::ClientConfiguration config;
    config.region = /*cut*/;

    auto client = Aws::MakeShared<Aws::S3::S3Client>("s3-get", config, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never);
 
    Aws::S3::Model::GetObjectRequest request;
    request.SetBucket(/*cut*/);
    request.SetKey(/*cut*/);

    auto outcome = client->GetObject(request); // Crashes here on specific S3 objects
    if (!outcome.IsSuccess()) {
        std::cerr << "Error " << static_cast<int>(outcome.GetError().GetErrorType()) << ": " << outcome.GetError().GetMessage().c_str() << "\n";
        return 1;
    }

    std::cout << outcome.GetResult().GetBody().rdbuf();
    return 0;
}

I've tested it against two files and get different failures. Note that I've tested it on macOS but it should be the same on any other platform.

Note that I've verified that if I run the above program for a "normal" S3 object I get one's content printed.

Case 1: Customer's S3 object is a particularly malformed XML

I got segmentation fault

S3 file entities.dat:

&#0</a>

Segmentation fault call stack:

Thread 1 Queue : com.apple.main-thread (serial)
#0	0x0000000100e0f4ce in Aws::External::tinyxml2::StrPair::GetStr() at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/external/tinyxml2/tinyxml2.cpp:201
#1	0x0000000100e10cd9 in Aws::External::tinyxml2::XMLNode::Value() const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/external/tinyxml2/tinyxml2.cpp:634
#2	0x0000000100ece0a6 in Aws::Utils::Xml::XmlNode::GetName() const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/utils/xml/XmlSerializer.cpp:59
#3	0x0000000100dafdd8 in Aws::Client::XmlErrorMarshaller::ContainsError(Aws::Http::HttpResponse const&) at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/client/AWSErrorMarshaller.cpp:122
#4	0x000000010035f1a2 in Aws::S3::S3Client::DoesResponseGenerateError(std::__1::shared_ptr<Aws::Http::HttpResponse> const&) const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-s3/source/S3Client.cpp:2800
#5	0x0000000100d99d58 in Aws::Client::AWSClient::AttemptOneRequest(Aws::Http::URI const&, Aws::AmazonWebServiceRequest const&, Aws::Http::HttpMethod, char const*) const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/client/AWSClient.cpp:295
#6	0x0000000100d9633c in Aws::Client::AWSClient::AttemptExhaustively(Aws::Http::URI const&, Aws::AmazonWebServiceRequest const&, Aws::Http::HttpMethod, char const*) const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/client/AWSClient.cpp:174
#7	0x0000000100d9c243 in Aws::Client::AWSClient::MakeRequestWithUnparsedResponse(Aws::Http::URI const&, Aws::AmazonWebServiceRequest const&, Aws::Http::HttpMethod, char const*) const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/client/AWSClient.cpp:341
#8	0x0000000100336c66 in Aws::S3::S3Client::GetObject(Aws::S3::Model::GetObjectRequest const&) const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-s3/source/S3Client.cpp:1441
#9	0x000000010000220e in main at /Users/andreyu/projects/aws-sdk-cpp/s3-get/s3-get.cpp:22
#10	0x00007fffaaf05235 in start ()

Case 2: Customer's S3 object is valid XML with Error root tag

S3 file error.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    whatever
</Error>

In debug version I get an assertion:

Assertion failed: (httpResponse->GetResponseCode() != HttpResponseCode::OK), function BuildAWSError, file /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/client/AWSClient.cpp, line 737.

The call stack is following:

#0	0x00007fffab033d42 in __pthread_kill ()
#1	0x00000001010b97a0 in pthread_kill ()
#2	0x00007fffaaf99420 in abort ()
#3	0x00007fffaaf60893 in __assert_rtn ()
#4	0x0000000100da3b53 in Aws::Client::AWSXMLClient::BuildAWSError(std::__1::shared_ptr<Aws::Http::HttpResponse> const&) const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/client/AWSClient.cpp:737
#5	0x0000000100d9a1d2 in Aws::Client::AWSClient::AttemptOneRequest(Aws::Http::URI const&, Aws::AmazonWebServiceRequest const&, Aws::Http::HttpMethod, char const*) const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/client/AWSClient.cpp:298
#6	0x0000000100d9633c in Aws::Client::AWSClient::AttemptExhaustively(Aws::Http::URI const&, Aws::AmazonWebServiceRequest const&, Aws::Http::HttpMethod, char const*) const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/client/AWSClient.cpp:174
#7	0x0000000100d9c243 in Aws::Client::AWSClient::MakeRequestWithUnparsedResponse(Aws::Http::URI const&, Aws::AmazonWebServiceRequest const&, Aws::Http::HttpMethod, char const*) const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-core/source/client/AWSClient.cpp:341
#8	0x0000000100336c66 in Aws::S3::S3Client::GetObject(Aws::S3::Model::GetObjectRequest const&) const at /Users/andreyu/projects/aws-sdk-cpp/aws-cpp-sdk-s3/source/S3Client.cpp:1441
#9	0x000000010000220e in main at /Users/andreyu/projects/aws-sdk-cpp/s3-get/s3-get.cpp:22
#10	0x00007fffaaf05235 in start ()

In release version program prints the error message as following rather than the S3 object content (so valid customer's S3 object is interpreted as an error from server):

Error 100: 

This case should happen if customer's S3 object is valid XML having one of the following:

  • Root element is Error
  • Root element is Errors
  • There is a root's child element Error
  • There is a root's child element Errors

Conditions are extracted from bool XmlErrorMarshaller::ContainsError(const Aws::Http::HttpResponse& httpResponse)

bool XmlErrorMarshaller::ContainsError(const Aws::Http::HttpResponse& httpResponse)

@dyfrgi
Copy link

dyfrgi commented Jan 22, 2018

Agreed re: cutting down the scope of where it's called. In our internal fix, which is different due to being on an older version, we only do this for calls that go via AttemptOneRequest and then only calls which are not GET, which is sufficient. It leaves open the possibility that future API calls will have the same behavior. It could be cut down even more and/or have a different entry point for where we check to see if it's a call which needs this additional check (besides AttemptOneRequest), but I strongly agree that limiting the scope just to calls where it's needed is a requirement. I share the same concern regarding performance. The memory use should also be much increased, especially for large objects.

I've asked the developer who worked on that here to share the solution on this bug.

@singku
Copy link
Contributor

singku commented Jan 25, 2018

Hi, we have updated TinyXml2 to the latest version and at the same time reverted the changes done in f5c0f79. We will find a more elegant solution for that problem. You should have no problem now on version 1.3.46 , please reopen it if you still have problem.

@singku singku closed this as completed Jan 25, 2018
@oliora
Copy link
Contributor Author

oliora commented Jan 26, 2018

Thank you, Andrew

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue is a bug.
Projects
None yet
Development

No branches or pull requests

4 participants