Titanium JIRA Archive
Appcelerator Community (AC)

[AC-6584] iOS: ti.identity rare crash when when reading/updating KeychainItem value

GitHub Issuen/a
TypeBug
Priorityn/a
StatusOpen
ResolutionUnresolved
Affected Version/sn/a
Fix Version/sn/a
ComponentsTitanium SDK & CLI
Labelsn/a
ReporterTeun Klijn
AssigneeAbir Mukherjee
Created2020-08-11T13:47:41.000+0000
Updated2020-08-11T13:47:41.000+0000

Description

*Summary* We're using ti.identity to store a user token in the iOS keychain, this goes well most of the time, but sometimes (about 7% of the time according to Firebase) an exception will occur:
Crashed: com.apple.root.default-qos
EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000000000020
__26-[APSKeychainWrapper read]_block_invoke

0  libobjc.A.dylib                0x1aec3bbf0 objc_retain + 16
1  MyApp                          0x10092b284 __26-[APSKeychainWrapper read]_block_invoke + 111 (APSKeychainWrapper.m:111)
2  libdispatch.dylib              0x1af488a38 _dispatch_call_block_and_release + 24
3  libdispatch.dylib              0x1af4897d4 _dispatch_client_callout + 16
4  libdispatch.dylib              0x1af42dc80 _dispatch_queue_override_invoke + 684
5  libdispatch.dylib              0x1af43a030 _dispatch_root_queue_drain + 372
6  libdispatch.dylib              0x1af43a8d4 _dispatch_worker_thread2 + 128
7  libsystem_pthread.dylib        0x1af66a1b4 _pthread_wqthread + 464
8  libsystem_pthread.dylib        0x1af66ccd4 start_wqthread + 4
*Step to reproduce*

Add the example code to a Titanium classic project

Add the ti.identity module

Run the app on iOS and wait until the app crashes (usually within a couple of minutes)

NOTE: the actual app doesn't loop to update the KeychainItem, this is just an example.
var TiIdentity = require('ti.identity');

var win = Ti.UI.createWindow({
  backgroundColor: '#fff',
});

var countLabel = Ti.UI.createLabel({
  width: Ti.UI.FILL,
  textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
});
win.add(countLabel);

function updateKeychainItem(value, onResult) {
  var keychainItem = TiIdentity.createKeychainItem({
    identifier: 'password',
  });

  keychainItem.fetchExistence(function (eExistence) {
    if (eExistence.exists) {
      keychainItem.addEventListener('read', function (eRead) {
        if (!eRead.success) {
          onResult(eRead);
          return;
        }

        if (eRead.value != value) {
          keychainItem.addEventListener('update', onResult);
          keychainItem.update(value);
        } else {
          onResult({ success: true });
        }
      });
      keychainItem.read();
    } else {
      keychainItem.addEventListener('save', onResult);
      keychainItem.save(value);
    }
  });
}

win.addEventListener('open', function () {
  (function updateLoop(i) {
    updateKeychainItem('pass ' + i, function (e) {
      if (--i) updateLoop(i);
      countLabel.text = i;
    });
  })(100000);
});

win.open();
*Expected result* The app updates the KeychainItem a 100000 times. *Actual result* The app crashes before finishing the loop.

Comments

No comments

JSON Source