c++aws-sdkunreal-engine4dynamic-cast

Aws::Delete() attempting dynamic_cast on pointer to zero page


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.


Solution

  • 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.