access violation in serviceBasic()

benjaml
benjaml
edited September 2019 in Native
Hello

I've come to this error, when I use service(), I got a read access violation, then I splited service() into the three function it is composed of and then I got this stack

ExitGames::Common::MemoryManagement::deallocate< ExitGames::Common::JString>(const ExitGames::Common::JString * p) Line 415 C++
ExitGames::Common::DictionaryBase::~DictionaryBase() Line 50 C++
ExitGames::Common::Dictionary< unsigned char,ExitGames::Common::Object>::~Dictionary<unsigned char,ExitGames::Common::Object>() Line 112 C++
[External Code]
ExitGames::Photon::PhotonPeer::serviceBasic() Line 183 C++
ExitGames::LoadBalancing::Client::serviceBasic() Line 708 C++
NetworkingLogic::run() Line 166 C++

I must have forgot something but I have no idea what maybe this will say something to someone. When I call this function I'm connected

Comments

  • Hi @benjaml.

    That looks like you somehow corrupted your memory in a way that has corrupted the callstack.

    This is how the implementation of PhotonPeer::serviceBasic() looks like:
    
    void PhotonPeer::serviceBasic(void)
    {
    	mspPeerBase->serviceBasic();
    }
    
    That function does not call Dictionary::~Dictionary() at all.

    I suggest that you try to reproduce this with one of our demos and then post the modifications to the demo code that you had to do to be able to reproduce it.
  • I've seen kinda same issue recently.

    ExitGames::Common::MemoryManagement::Internal::Interface::free(void*) 0x00000000a1d208ec ExitGames::Common::JVector<ExitGames::Common::Object>::~JVector() 0x00000000a1d18b4e ExitGames::Common::Hashtable::~Hashtable() 0x00000000a1d1908c ExitGames::Common::DictionaryBase::~DictionaryBase() 0x00000000a1d17d3a ExitGames::Common::Dictionary<unsigned char, ExitGames::Common::Object>::~Dictionary() 0x00000000a1cde4ea ExitGames::Photon::OperationResponse::~OperationResponse() 0x00000000a1cefc8e ExitGames::Photon::Internal::PeerBase::deserializeOperationResponse(unsigned char*, bool, int, unsigned char) 0x00000000a1cfc47e ExitGames::Photon::Internal::PeerBase::deserializeOperation(unsigned char*, int) 0x00000000a1cfc098 ExitGames::Photon::Internal::EnetPeer::dispatchIncomingCommands() 0x00000000a1cf715e ExitGames::Photon::Internal::PeerBase::service(bool) 0x00000000a1cfb0f8 ExitGames::Photon::PhotonPeer::service(bool) 0x00000000a1cf07ea ExitGames::LoadBalancing::Client::service(bool) 0x00000000a1cd6864

    Happens to me only when I setData in the AuthenticationValues class, with an anonymous authentication it works fine.
  • Hi @abelthefirst.

    Can you reproduce this with one of our demos by just adding you auth code and leaving everything else the same?
  • Hi @Kaiserludi
    Together with @abelthefirst we've been able to create sample code showing this issue.
    This code reproduces behavior of Peer::opAuthenticate method in part where it logs its data.

    Crash log
    
    
    signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x39
    Cause: null pointer dereference
    	eax 00000039  ebx 8ef01bc8  ecx 8ef01bc8  edx ffffffff
    	esi 0000110d  edi 9798d4d0
    	xcs 00000073  xds 0000007b  xes 0000007b  xfs 0000003b  xss 0000007b
    	eip 8eec7a11  ebp bf86eb98  esp bf86eb80  flags 00210282
    backtrace:
    	#00 pc 000c3a11  /data/app/tacx.android.staging-L0ExR2DMBKCdfJyZb1gmmg==/lib/x86/libLoadBalancing.so (ExitGames::Common::MemoryManagement::Internal::Interface::free(void*)+21)
    	#01 pc 0009bd66  /data/app/tacx.android.staging-L0ExR2DMBKCdfJyZb1gmmg==/lib/x86/libLoadBalancing.so (_ZN9ExitGames6Common16MemoryManagement15deallocateArrayIcEEvPKT_+70)
    	#02 pc 000c9e58  /data/app/tacx.android.staging-L0ExR2DMBKCdfJyZb1gmmg==/lib/x86/libLoadBalancing.so (ExitGames::Common::UTF8String::~UTF8String()+46)
    	#03 pc 000c87cb  /data/app/tacx.android.staging-L0ExR2DMBKCdfJyZb1gmmg==/lib/x86/libLoadBalancing.so (EG_vswprintf(wchar_t*, unsigned int, wchar_t const*, char*)+2001)
    	#04 pc 000c348a  /data/app/tacx.android.staging-L0ExR2DMBKCdfJyZb1gmmg==/lib/x86/libLoadBalancing.so (ExitGames::Common::Logger::vlog(int, wchar_t const*, wchar_t const*, bool, unsigned int, wchar_t const*, char*) const+2018)
    	#05 pc 000c2c8e  /data/app/tacx.android.staging-L0ExR2DMBKCdfJyZb1gmmg==/lib/x86/libLoadBalancing.so (ExitGames::Common::Logger::log(int, wchar_t const*, wchar_t const*, bool, unsigned int, wchar_t const*, ...) const+126)
    	#06 pc 000631be  /data/app/tacx.android.staging-L0ExR2DMBKCdfJyZb1gmmg==/lib/x86/libLoadBalancing.so (PhotonUtils::doCrash()+670)
    	#07 pc 00062e6a  /data/app/tacx.android.staging-L0ExR2DMBKCdfJyZb1gmmg==/lib/x86/libLoadBalancing.so (PhotonUtils::PhotonUtils(_JNIEnv*)+1146)
    	#08 pc 0005e534  /data/app/tacx.android.staging-L0ExR2DMBKCdfJyZb1gmmg==/lib/x86/libLoadBalancing.so (JNILoadBalancingListener::JNILoadBalancingListener(_JNIEnv*, _jobject*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*, _jmethodID*)+388)
    	#09 pc 0005c34b  /data/app/tacx.android.staging-L0ExR2DMBKCdfJyZb1gmmg==/lib/x86/libLoadBalancing.so (Java_com_tacx_photon_LoadBalancing_start+1563)
    	#10 pc 0005ee08  /data/app/tacx.android.staging-L0ExR2DMBKCdfJyZb1gmmg==/oat/x86/base.odex (offset 0x5e000)
    	#11 pc 000d8fff  [anon:libc_malloc:b2880000]
    	#12 pc 000cc787  /dev/ashmem/dalvik-main space (region space) (deleted) (offset 0x540000)
    
    


    Sample code
    #include "Photon-cpp/inc/OperationRequest.h"
    #include "LoadBalancing-cpp/inc/Internal/Enums/ParameterCode.h"
    #include "LoadBalancing-cpp/inc/Internal/Enums/OperationCode.h"
    #include "LoadBalancing-cpp/inc/AuthenticationValues.h"
    #include "Common-cpp/inc/Base.h"
    #include "Common-cpp/inc/Logger.h"
    
    using namespace ExitGames::Common;
    using namespace ExitGames::Photon;
    using namespace ExitGames::LoadBalancing;
    using namespace ExitGames::LoadBalancing::Internal;
    
    class LoggerListener : public ExitGames::Common::BaseListener {
        virtual void debugReturn(int debugLevel, const ExitGames::Common::JString& str) {}
    };
    
    void MyClass::doCrash() {
    
        LoggerListener loggerListener;
        Logger mLogger(DebugLevel::ALL);
        mLogger.setListener(loggerListener);
    
    	// This code reproduces behavior of Peer::opAuthenticate method where it logs its data.
    
        char *cToken = "eyJhbGciOiJSUzI1NiIsIng1dCI6InZad29adEpCZUpBOGJZYWxtTXRCcE91Qm0wND0iLCJ0eXAiOiJKV1QifQ.eyJzdWIiOiJhZG1pbi1zZGtAdGFjeC1jbG91ZC1zdGFnaW5nLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwiYXVkIjoiaHR0cHM6Ly9pZGVudGl0eXRvb2xraXQuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLmlkZW50aXR5LmlkZW50aXR5dG9vbGtpdC52MS5JZGVudGl0eVRvb2xraXQiLCJ1aWQiOiJkMjhmZGRhMi00NWUwLTQ2MGEtODRlZS1mNWRmNmJlNTc1YjciLCJzdWJzY3JpcHRpb25UeXBlIjoicHJlbWl1bUhkIiwic3Vic2NyaXB0aW9uRW5kRGF0ZSI6MTU3MjM0MjkzNywiaXNzIjoiYWRtaW4tc2RrQHRhY3gtY2xvdWQtc3RhZ2luZy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsInN1YnNjcmlwdGlvbklzQWN0aXZlIjoidHJ1ZSIsImV4cCI6MTU2OTg4MTE1OCwibGl2ZU9wcG9uZW50c0NvbnNlbnQiOnRydWUsImlhdCI6MTU2OTg3NzU1OH0.GSaGl2R0wjBFx9EAJfxIElYBzj3IXTnHpDk3rxlLi_uEjWdqmGFw2PsPwocPygElxaiMeaARqoL-tSeZRcFs50o64ySgrYvpINqrPsKrnn4IMvQsBSzdbqFaqHH24Kx4xrWusPm2h9jzOHY-35lDMkbHlrNZYLG6yEiypkHiuRhrCHbW9zewa7iL0J8Be0y1-aLUo4OiUfe66eAiJfhWYCDJQgCgKHQBqdxw-cC5Vf5nPE6tuJCOiexDakEtQ2d55XsFEaQ7L3aK8BxyFdnyDeBexDDYlM6oz-VSqX0AU9gMH5qcR-NSXcFCcZHOSssvOZ7rqQVnySix8G8ctMCDbQ";
        JVector<nByte> utfByteVector(reinterpret_cast<const nByte *>(cToken), strlen(cToken), strlen(cToken));
    
        OperationRequestParameters op;
        op.put(ParameterCode::CLIENT_AUTHENTICATION_DATA, ValueObject<const nByte*>(utfByteVector.getCArray(), static_cast<int>(utfByteVector.getSize())));
        EGLOG(DebugLevel::INFO, OperationRequest(OperationCode::AUTHENTICATE, op).toString(true));
    }



  • Hi @tos_tacx.

    I have added your repro-code to demo_loadBalancing and rand that function from within NetworkLogic::connect(), but it does not crash for me. On which platform have you tested this?

    Note that the line
    EGLOG(DebugLevel::INFO, OperationRequest(OperationCode::AUTHENTICATE, op).toString(true))
    contains a bug.

    The correct version of that line needs to be like this (note the 'cstr()'):

    EGLOG(DebugLevel::INFO, OperationRequest(OperationCode::AUTHENTICATE, op).toString(true).cstr())

    Can you still reproduce the crash when you apply that fix?

    I am aware that you have copied that line from the implementation code of Peer::opAuthenticate() in Peer.cpp.
    We will fix it there as well with the next release. Until then you can of course simply apply that fix and recompile the LoadBalancing-cpp lib yourself.
  • I used x86 and arm Android.
    iOS works just fine.
  • Also crash can not be reproduced every time, but only in around 50% of cases
  • Hi @tos_tacx.

    So does it help to add the cstr() call, that I have mentioned in my previous code or do you still experience the crash even after applying that change?
  • Hi @Kaiserludi ,

    I tried to apply the fix you suggested and to rebuild the LoadBalancing lib, but it didn't help, unfortunately. So the only workaround for now is to set the lower DebugLevel value.

    Thank you,
    Alex
  • Kaiserludi
    Kaiserludi admin
    edited October 2019
    Hi @abelthefirst , hi @tos_tacx.

    I finally found the time to have an in-depth look at your reported issue today.

    I could reproduce the crash and have successfully tracked down the cause of it.
    On Android the maximum length for the string that can be passed to EGLOG() is 4096 characters including the null-terminator.
    The string that is produced by OperationRequest(OperationCode::AUTHENTICATE, op).toString(true) with your example token is longer than that.


    As a workaround for now you can rebuild LoadBalancing-cpp after you have changed the code to store the return value of OperationRequest(OperationCode::AUTHENTICATE, op).toString(true) in a JString instance, and then do 2 calls to EGLOG() with two sub strings of that string, which are on their own each below 4096 characters in length.

    We will lift the 4096 characters limit with the next release, so that starting with 4.1.15.0 no such workaround will be needed anymore.

    See below for an example application of the workaround to your doCrash()-reproduction case:
    
    ExitGames::Common::JString str = OperationRequest(OperationCode::AUTHENTICATE, op).toString(true);
    ExitGames::Common::JString str0 = str.substring(0, 2048);
    ExitGames::Common::JString str1 = str.substring(2048);
    
    EGLOG(DebugLevel::INFO, str0.cstr());
    EGLOG(DebugLevel::INFO, str1.cstr());
    
  • PS:
    I was wrong about the .cstr() by the way. The code inside Peer::opAuthenticate() is correct. No .cstr() is required as JString::operator const EG_CHAR* (void) const will be called implicitly for a JString that is passed to EGLOG as the format string. Explicit calls to cstr() for EGLOG() arguments are only necessary for the variadic arguments that correspond to a L"%ls" in the format string.
  • Hi @abelthefirst , hi @tos_tacx.

    4.1.15.0 has been released today.