0xsequence / sequence-unreal

Sequence Unreal SDK
Apache License 2.0
4 stars 2 forks source link

Cr.sav corruption engine crash #166

Open BellringerQuinn opened 3 weeks ago

BellringerQuinn commented 3 weeks ago

The credentials are currently saved to a Cr.sav file located at /Saved/SaveGames/Cr.sav

If something happens that corrupts or modifies the Cr.sav file from the format that is expected, the engine will crash whenever we try to access the wallet or credentials and give a rather unhelpful stack trace.

The current workaround is to delete the Cr.sav file. The dev will need to login again as their credentials will no longer be cached (not a big deal). In the current state, the engine will continue to crash until the Cr.sav file is deleted.

We should catch the exception when reading credentials so that the engine doesn't crash and delete the Cr.sav file. This way integrators are not caught with an annoying and hard to debug crash.

BellringerQuinn commented 2 weeks ago

I was able to replicate this bug by modifying the Cr.sav file in a hex editor - it took some trial and error, so attaching my Cr.sav file (as Google Drive link)

Some things I noticed that may be helpful:

In Authenticator.cpp there is:

bool UAuthenticator::GetStoredCredentials(FCredentials_BE* Credentials) const
{
    bool ret = false;
    if (const UStorableCredentials* LoadedCredentials = Cast<UStorableCredentials>(UGameplayStatics::LoadGameFromSlot(this->SaveSlot, this->UserIndex)))
    {
        FString CTR_Json = "";
        if (Encryptor)
        {//Use set encryptor
            CTR_Json = Encryptor->Decrypt(LoadedCredentials->EK); // this line throws
        }
        else
        {//Use the fallback
            CTR_Json = USequenceEncryptor::Decrypt(LoadedCredentials->EK, LoadedCredentials->KL);
        }

        ret = USequenceSupport::JSONStringToStruct<FCredentials_BE>(CTR_Json, Credentials);
        ret &= Credentials->RegisteredValid();
    }
    return ret;
}

It seems as though NativeAppleEncryptor.mm is throwing the exception and the exception is not propagating up the stack for some reason out into our unreal code (tested by wrapping the GetStoredCredentials logic in a try catch block). We likely need to handle the exception in the .mm file code; my assumption is that there is some sort of barrier (of sorts) between Unreal and the native code that prevents the exceptions from propagating up the stack to where we can catch and handle it effectively. We'll likely need to handle the exception in the native code.

When I used the debugger, I kept getting stuck infinitely on this line CFDataRef cfDecryptedData = SecKeyCreateDecryptedData(PrivateKeyRef,algorithm,plainText,&error); At first, I thought this was causing a stack overflow error, but I think it may just be that the Rider debugger doesn't know how to handle itself in this scenario. Especially since my minidump.dmp file reveals a separate exception

My minidump.dmp file reveals the exception is: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'SecKeyCreateDecryptedData() called with NULL ciphertext' Full file attached: minidump.dmp

BellringerQuinn commented 2 weeks ago

Note: while the above exception appears to be a Mac issue, it is likely that the same Cr.sav will lead to a crash on the windows side as well - worth checking.

BellringerQuinn commented 2 weeks ago

Also uploading a .rtf with the hex pasted in just in case future readers are unable to download and use the .sav file above from google drive directly

BellringerQuinn commented 2 weeks ago

It is possible that the exceptions are not propagating up the stack due to a lack of pre-processor directive or macro

We should investigate this - I don't have a lot of context, but from what I understand it is possible that in some cases the Unreal compiler will just ignore try catch blocks during compilation unless configured appropriately https://forums.unrealengine.com/t/try-catch-fails-to-catch-exception/463146/3