I am using the AWS SDK for C++ to access S3 buckets. This is as part of a larger Unreal Engine project. As such, the AWS SDK is using the Unreal Engine allocators, hooked in via Aws::Utils::Memory::MemorySystemInterface
. When one of my requests goes out of scope (and is destroyed), I get a segmentation fault in Aws::Delete()
. This only occurs for a specific GetObject
request where i've specified a stream type for the request.
The code for the request that breaks is below:
Aws::S3::Model::GetObjectRequest request;
request.SetBucket(bucket);
request.SetKey(filename);
request.SetResponseStreamFactory([destination_file]()
{return Aws::New<Aws::FStream>("getdata", destination_file, std::ios_base::out | std::ios_base::binary); });
//^ suspect this line to be causing problems, somehow.
Aws::S3::Model::GetObjectOutcome outcome = s3.GetObject(request);
if (!outcome.IsSuccess())
{
Completed.Broadcast(fullpath, false);
return;
}
else
{
fullpath = to_file;
Completed.Broadcast(fullpath, true);
return;
}
//request and outcome leave scope here, triggering an exception.
The request does complete successfully (outcome.IsSuccess()
is true
) and the file appears in the correct place. Once either outcome
or request
(i'm not sure which) leaves scope is where it crashes:
Exception thrown at 0x00007FFA0CC2612A (vcruntime140.dll) in UnrealEditor.exe:
0xC0000005: Access violation reading location 0x0000000000000068.
with the following call stack:
vcruntime140.dll!00007ffa0cc2612a() Unknown
[Inline Frame] aws-cpp-sdk-core.dll!Aws::Delete(std::basic_iostream<char,std::char_traits<char>> * pointerToT) Line 117 C++
aws-cpp-sdk-core.dll!Aws::Utils::Stream::ResponseStream::ReleaseStream() Line 62 C++
aws-cpp-sdk-core.dll!Aws::Utils::Stream::ResponseStream::~ResponseStream() Line 54 C++
UnrealEditor-Plugin.dll!UPlugin::Activate() Line 56 C++
Specifically, the exception is triggered here in AwsMemory.h
:
template<typename T>
typename std::enable_if<std::is_polymorphic<T>::value>::type Delete(T* pointerToT)
{
if (pointerToT == nullptr)
{
return;
}
// deal with deleting objects that implement multiple interfaces
// see casting to pointer to void in http://en.cppreference.com/w/cpp/language/dynamic_cast
// https://stackoverflow.com/questions/8123776/are-there-practical-uses-for-dynamic-casting-to-void-pointer
// NOTE: on some compilers, calling the destructor before doing the dynamic_cast affects how calculation of
// the address of the most derived class.
void* mostDerivedT = dynamic_cast<void*>(pointerToT); // <-- exception
pointerToT->~T();
Free(mostDerivedT);
}
ReleaseStream()
and ResponseStream()
seem to just be wrappers for Aws::Delete
for this particular instance, the only thing of note being that ReleaseStream
flushes the stream before calling Delete
. There's nothing that suggests pointerToT
should be invalidated before the dynamic_cast
, and yet it is. This should all be occurring on a single thread. I have also verified that all the relevant locals (like destination_file
) are valid.
What could be causing this behaviour? This project has done other requests (List, Put & Get) without issue, although none of them have specified a stream factory.
UE4 is compiled without RTTI, so (obviously) dynamic_cast
does not work. I believe this was the issue. There is a new problem with calling Delete
on polymorphic types without using the dynamic_cast<void*>
trick.