qonversion / qonversion-ios-sdk

iOS SDK for cross-platform in-app purchase and subscription infrastructure, revenue analytics, engagement automation, and integrations
https://qonversion.io
MIT License
282 stars 23 forks source link

Crash on Launch in +[QNUserInfo bundle] #458

Closed goodones-mac closed 1 month ago

goodones-mac commented 7 months ago

Hi your SDK crashes on launch for us because of some issue with NSPredicate? It's happened about 20 times to our users according to sentry, but we haven't seen it in our Xcode builds. Might be a 'security' issue because you query all bundles and apple is getting more restrictive with bundle querying outside of your own app now and _predicateSecurityAction is in the stack?

The function in question in your library:

+ (nullable NSBundle *)bundle {
  NSPredicate *predicate = [NSPredicate predicateWithFormat:@"appStoreReceiptURL != nil"];
  return [NSBundle.allBundles filteredArrayUsingPredicate:predicate].firstObject;
}

Our code in the stack:

enum QonversionManager {
    static func start() {
        guard FeatureFlags.Engineering.useQonversionFrozenValue else { return }
        let config = Qonversion.Configuration(projectKey: ThirdPartySDKKeys.qonversionSdkKey, launchMode: .analytics)
        Qonversion.initWithConfig(config)
        QonversionSwift.shared.syncStoreKit2Purchases()
        Qonversion.shared().collectAppleSearchAdsAttribution()
    }
    // ...
}
SIGABRT: expressionValueWithObject:context: > selector
  libsystem_kernel    0x1bb28ebbc  __pthread_kill
  libsystem_pthread   0x1dbd1c850  pthread_kill
  libsystem_c         0x18b0a56a8  abort
  Foundation          0x1821f66c4  +[_NSPredicateUtilities _predicateSecurityAction]
  Foundation          0x18209d6e8  -[NSFunctionExpression expressionValueWithObject:context:]
  Foundation          0x182088318  -[NSComparisonPredicate evaluateWithObject:substitutionVariables:]
  Foundation          0x1820a4020  _filterObjectsUsingPredicate
  Foundation          0x18208d548  -[NSArray(NSPredicateSupport) filteredArrayUsingPredicate:]
  GoodOnes            0x1051f1170  +[QNUserInfo bundle] (QNUserInfo.m:99)
  GoodOnes            0x1051f0fe8  +[QNUserInfo appStoreReceipt] (QNUserInfo.m:76)
  GoodOnes            0x1051f0bdc  +[QNUserInfo overallData] (QNUserInfo.m:17)
  GoodOnes            0x1051e75e4  -[QNAPIClient launchRequest:] (QNAPIClient.m:133)
  GoodOnes            0x1051de9f0  -[QNProductCenterManager launch:] (QNProductCenterManager.m:727)
  GoodOnes            0x1051da488  -[QNProductCenterManager launchWithCompletion:] (QNProductCenterManager.m:176)
  GoodOnes            0x1051d4ffc  -[Qonversion launchWithKey:completion:] (Qonversion.m:96)
  GoodOnes            0x1051d4cd4  +[Qonversion initWithConfig:] (Qonversion.m:62)
  GoodOnes            0x10512d138  QonversionManager.start (QonversionManager.swift:10)
  GoodOnes            0x104e4fb44  QonversionManager.start (<compiler-generated>)
  GoodOnes            0x104e4fb44  Logger.initialize (Logger.swift:63)
  GoodOnes            0x104dd2460  AppDelegate.application (AppDelegate.swift:26)
  GoodOnes            0x104dd2748  AppDelegate.application (<compiler-generated>)
  SwiftUI             0x1880b8e70  AppDelegate.application
  SwiftUI             0x1880ae2c0  AppDelegate.application
  UIKitCore           0x182f6b5cc  -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:]
  UIKitCore           0x18313fda0  -[UIApplication _callInitializationDelegatesWithActions:forCanvas:payload:fromOriginatingProcess:]
  UIKitCore           0x1831294c4  -[UIApplication _runWithMainScene:transitionContext:completion:]
  UIKitCore           0x182f9731c  -[_UISceneLifecycleMultiplexer completeApplicationLaunchWithFBSScene:transitionContext:]
  UIKitCore           0x182f967f4  -[UIApplication _compellApplicationLaunchToCompleteUnconditionally]
  UIKitCore           0x183147a6c  -[UIApplication _run]
  UIKitCore           0x182ee0fd0  UIApplicationMain
  SwiftUI             0x188199d64  KitRendererCommon
  SwiftUI             0x1880e4838  runApp<T>
  SwiftUI             0x1880ca1d8  App.main
  GoodOnes            0x104ded900  GoodOnesApp.$main (GoodOnesApp.swift)
  GoodOnes            0x104ded900  main (GoodOnesApp.swift)
  0x106bf84d0  <redacted>
suriksarkisyan commented 7 months ago

Hi, @goodones-mac Thanks for contacting us. Yes, it looks like some issue inside the Apple NSPredicate logic is unrelated to our code, but I can't be sure because it's almost impossible to reproduce for us. Do you have some clues on how we can reproduce the crash?

goodones-mac commented 7 months ago

No we don't, we just saw it in sentry. 9 users + 20 instances. We are also not a large app so I think it will show up a lot more in bigger apps.

suriksarkisyan commented 7 months ago

Could you please contact our technical support via Intercom (using our site)? I'll ask you for more details to help us understand the crash.

saagarjha commented 7 months ago

This is most likely not a bug in your SDK, but a user who is running this on a jailbroken device.

NSPredicate is the frequent target for exploit developers because it provides an easy and reliable way to convert security bugs into code execution. While it is difficult for them to restrict the API for third parties, Apple has started putting security checks in place that tries to block “suspicious” use of NSPredicate in their own apps. Whether these checks are effective or not is a different question (mostly not) but it does mean that constructs like the one in this SDK are flagged by them.

Of course, what Apple does in their own apps is not supposed to have any effect on third-party apps like yours. Unfortunately, the way Apple enables these is by checking the process belongs to a platform application (vaguely, is it Apple signed and shipped with the system). If that is the case this additional validation enables itself. However, this is not the only thing that uses this platform application flag: there are all sorts of privileged and system behaviors that can only be performed by a platform application on iOS.

A jailbreak needs the same level of access, of course. In theory it should grant itself these privileges and run third party apps as-is, but for various reasons most jailbreaks these day are sloppy and mark everything running on the system as a platform application. Usually having more privileges than normal is not a problem because third-party apps do not typically notice. However, Apple tries to enable the security feature on processes they believe are theirs and so when the jailbreak lies about this it ends up turning on for everyone. This is what is crashing your code.

saagarjha commented 7 months ago

Just to round out the discussion I would also like to say that it’s probably not a good idea to try to detect jailbreak users or similar to work around this. My recommendation would be to avoid using NSPredicate here, which should be fairly straightforward to implement and also more efficient to boot. And if you’re looking to do even better I wouldn’t recommend using +allBundles at all, it’s probably overkill for what you’re doing. If you just need the main app binary, using another method (such as _NSGetExecutablePath) is probably a better bet since it doesn’t need to load all bundles.

github-actions[bot] commented 7 months ago

This issue is stale because it has been open 7 days with no activity. Remove stale label or comment or this will be closed in 5 days.

goodones-mac commented 7 months ago

@suriksarkisyan this is kind of a short time to auto-close an open issue like this, wondering if we can keep it open so it's tracked properly?

suriksarkisyan commented 7 months ago

Hi, @goodones-mac I'll let this issue open until we fix it or find the reason for the crash, even if it does not depend on our SDK. I'm not sure, but I'll try to find another solution for our case. Let's keep in touch!

github-actions[bot] commented 7 months ago

This issue is stale because it has been open 7 days with no activity. Remove stale label or comment or this will be closed in 5 days.

arcopo commented 3 months ago

I'm facing this issue July 1st, has it not been resolved any further?

suriksarkisyan commented 1 month ago

Hi there. We updated our SDK and released 5.12.2 a fix for this issue. As discussed above, this issue is on the Apple side and related to jailbroken devices. I hope this fix will help you never face this issue again. Let's keep in touch!