What could be the cause of an occasional failure to create an encrypted Realm object?

Viewed 633

I've got a released app using Realm and there are some crash logs showing that there is sometimes a failure to create the realm with a configuration resulting in a EXC_BREAKPOINT (SIGTRAP) crash. (there's 9 crash files for a few hundred app installations so its not something which is happening frequently)

@objc class Database : NSObject
{    
    let configuration = Realm.Configuration(encryptionKey: Database.getKey() as Data)
    var transactionRealm:Realm? = nil

    override init()
    {
        let realm = try! Realm(configuration: configuration) // Crash here
        <snip>
    }

    // This getKey() method is taken from the Realm website
    class func getKey() -> NSData {
        let keychainIdentifier = "Realm.EncryptionKey.AppKey"
        let keychainIdentifierData = keychainIdentifier.data(using: String.Encoding.utf8, allowLossyConversion: false)!

        // First check in the keychain for an existing key
        var query: [NSString: AnyObject] = [
            kSecClass: kSecClassKey,
            kSecAttrApplicationTag: keychainIdentifierData as AnyObject,
            kSecAttrKeySizeInBits: 512 as AnyObject,
            kSecReturnData: true as AnyObject
        ]

        // To avoid Swift optimization bug, should use withUnsafeMutablePointer() function to retrieve the keychain item
        // See also: http://stackoverflow.com/questions/24145838/querying-ios-keychain-using-swift/27721328#27721328
        var dataTypeRef: AnyObject?
        var status = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) }
        if status == errSecSuccess {
            return dataTypeRef as! NSData
        }

        // No pre-existing key from this application, so generate a new one
        let keyData = NSMutableData(length: 64)!
        let result = SecRandomCopyBytes(kSecRandomDefault, 64, keyData.mutableBytes.bindMemory(to: UInt8.self, capacity: 64))
        assert(result == 0, "REALM - Failed to get random bytes")

        // Store the key in the keychain
        query = [
            kSecClass: kSecClassKey,
            kSecAttrApplicationTag: keychainIdentifierData as AnyObject,
            kSecAttrKeySizeInBits: 512 as AnyObject,
            kSecValueData: keyData,
            kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock
        ]

        status = SecItemAdd(query as CFDictionary, nil)
        assert(status == errSecSuccess, "REALM - Failed to insert the new key in the keychain")

        return keyData
    }

Here's the relevant portion of the crash file:

Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x0000000103c0f30c
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [0]
Triggered by Thread:  0

Thread 0 name:
Thread 0 Crashed:
0   libswiftCore.dylib              0x0000000103c0f30c 0x103ab4000 + 1422092
1   libswiftCore.dylib              0x0000000103c0f30c 0x103ab4000 + 1422092
2   libswiftCore.dylib              0x0000000103b13d2c 0x103ab4000 + 392492
3   libswiftCore.dylib              0x0000000103b13bf4 0x103ab4000 + 392180
4   My app                          0x000000010334ff14 _TFC14Caller_Name_ID8DatabasecfT_S0_ + 1648 (Database.swift:21)
5   My app                          0x0000000103330624 -[Model createDatabase] + 200 (Model.m:271)

Line 21 of the Database.swift file is the one indicated in the code above in the Init() method.

If the reason for the Realm crash is because getKey() fails, then why would that be failing? If thats not the cause, that what are other reasons for the failure? And is there anything the code can do (such as retry to create the Realm object) if there is a failure?

1 Answers
Related