firebase / firebase-ios-sdk

Firebase SDK for Apple App Development
https://firebase.google.com
Apache License 2.0
5.62k stars 1.48k forks source link

Exception and crash due to forwarding class AndroidConfigFetchProto which is neither defined, nor implemented #4334

Closed elena-krakhmalova closed 4 years ago

elena-krakhmalova commented 4 years ago

[READ] Step 1: Are you in the right place?

Yes, seems to me in the right place.

[REQUIRED] Step 2: Describe your environment

Environment

[REQUIRED] Step 3: Describe the problem

Steps to reproduce:

  1. Download Firebase quickstart sample project for iOS
  2. Add GoogleService-Info.plist into quickstart sample project with valid keys, so that project can launch without issues
  3. Install pods
  4. Replace source in AppDelegate.m with code from AppDelegate.txt attached
  5. Run ConfigExample target on iOS simulator
  6. Observe app crashes due to exception. Please see Xcode logs in file attached

Please note in this example app crashes for simulator, but the same will happen for device as well. The real problem is that AndroidConfigFetchProto class is neither defined, nor implemented but referenced in Config.pbobjc.h and Config.pbobjc.m in FirebaseRemoteConfig pod.

Relevant Code:

xcode_log_crash.txt AppDelegate.txt

google-oss-bot commented 4 years ago

I found a few problems with this issue:

morganchen12 commented 4 years ago

@dmandar looks like we have a few references to AndroidConfigFetchProto. The stack trace doesn't show any symbols from the quickstart sample.

2019-11-18 00:15:53.983113+0200 ConfigExample[2859:108696] *** Assertion failure in -[GPBFieldDescriptor initWithFieldDescription:includesDefault:syntax:], /Volumes/Data/Projects/Heyzap/HZIOS-1074_investigation/quickstart-ios/config/Pods/Protobuf/objectivec/GPBDescriptor.m:532
2019-11-18 00:15:53.987145+0200 ConfigExample[2859:108696] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Class AndroidConfigFetchProto not defined'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff23c4f02e __exceptionPreprocess + 350
    1   libobjc.A.dylib                     0x00007fff50b97b20 objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff23c4eda8 +[NSException raise:format:arguments:] + 88
    3   Foundation                          0x00007fff256c9b61 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191
    4   protobuf                            0x0000000108fe639e -[GPBFieldDescriptor initWithFieldDescription:includesDefault:syntax:] + 670
    5   protobuf                            0x0000000108fe3496 +[GPBDescriptor allocDescriptorForClass:rootClass:file:fields:fieldCount:storageSize:flags:] + 342
    6   ConfigExample                       0x0000000108a858e4 +[RCNConfigFetchRequest descriptor] + 180
    7   protobuf                            0x000000010901fbd7 +[GPBMessage initialize] + 183
    8   libobjc.A.dylib                     0x00007fff50b98103 CALLING_SOME_+initialize_METHOD + 17
    9   libobjc.A.dylib                     0x00007fff50b98ee9 initializeNonMetaClass + 624
    10  libobjc.A.dylib                     0x00007fff50b994ba _ZL24initializeAndMaybeRelockP10objc_classP11objc_objectR8mutex_ttILb0EEb + 157
    11  libobjc.A.dylib                     0x00007fff50ba3a5d lookUpImpOrForward + 595
    12  libobjc.A.dylib                     0x00007fff50b94219 _objc_msgSend_uncached + 73
    13  libobjc.A.dylib                     0x00007fff50ba3b2b _ZL20resolveMethod_lockedP11objc_objectP13objc_selectorP10objc_classi + 184
    14  libobjc.A.dylib                     0x00007fff50ba39de lookUpImpOrForward + 468
    15  libobjc.A.dylib                     0x00007fff50ba37be class_getInstanceMethod + 47
    16  ConfigExample                       0x0000000108a44c47 +[AppDelegate isRegisteredClass:] + 55
    17  ConfigExample                       0x0000000108a44e31 +[AppDelegate registeredClasses] + 417
    18  ConfigExample                       0x0000000108a44f1a -[AppDelegate application:didFinishLaunchingWithOptions:] + 106
    19  UIKitCore                           0x00007fff478467a8 -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 232
    20  UIKitCore                           0x00007fff478481b7 -[UIApplication _callInitializationDelegatesWithActions:forCanvas:payload:fromOriginatingProcess:] + 3980
    21  UIKitCore                           0x00007fff4784dd06 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1281
    22  UIKitCore                           0x00007fff46f843e9 -[_UISceneLifecycleMultiplexer completeApplicationLaunchWithFBSScene:transitionContext:] + 122
    23  UIKitCore                           0x00007fff47471c01 _UIScenePerformActionsWithLifecycleActionMask + 83
    24  UIKitCore                           0x00007fff46f84efb __101-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]_block_invoke + 198
    25  UIKitCore                           0x00007fff46f8490a -[_UISceneLifecycleMultiplexer _performBlock:withApplicationOfDeactivationReasons:fromReasons:] + 296
    26  UIKitCore                           0x00007fff46f84d28 -[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:] + 818
    27  UIKitCore                           0x00007fff46f845bd -[_UISceneLifecycleMultiplexer uiScene:transitionedFromState:withTransitionContext:] + 345
    28  UIKitCore                           0x00007fff46f88beb __186-[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block_invoke_2 + 178
    29  UIKitCore                           0x00007fff473976bb +[BSAnimationSettings(UIKit) tryAnimatingWithSettings:actions:completion:] + 865
    30  UIKitCore                           0x00007fff4749053f _UISceneSettingsDiffActionPerformChangesWithTransitionContext + 240
    31  UIKitCore                           0x00007fff46f88906 __186-[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block_invoke + 153
    32  UIKitCore                           0x00007fff47490442 _UISceneSettingsDiffActionPerformActionsWithDelayForTransitionContext + 84
    33  UIKitCore                           0x00007fff46f88774 -[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:] + 381
    34  UIKitCore                           0x00007fff46ddf143 __64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke + 657
    35  UIKitCore                           0x00007fff46dddcc8 -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:] + 248
    36  UIKitCore                           0x00007fff46ddee6d -[UIScene scene:didUpdateWithDiff:transitionContext:completion:] + 210
    37  UIKitCore                           0x00007fff4784c363 -[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 535
    38  UIKitCore                           0x00007fff473b922d -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 361
    39  FrontBoardServices                  0x00007fff36555225 -[FBSSceneImpl _callOutQueue_agent_didCreateWithTransitionContext:completion:] + 442
    40  FrontBoardServices                  0x00007fff3657b598 __86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke.154 + 102
    41  FrontBoardServices                  0x00007fff3655fd05 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 220
    42  FrontBoardServices                  0x00007fff3657b229 __86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke + 355
    43  libdispatch.dylib                   0x00000001090ebd48 _dispatch_client_callout + 8
    44  libdispatch.dylib                   0x00000001090eecb9 _dispatch_block_invoke_direct + 300
    45  FrontBoardServices                  0x00007fff365a143e __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
    46  FrontBoardServices                  0x00007fff365a112c -[FBSSerialQueue _queue_performNextIfPossible] + 441
    47  FrontBoardServices                  0x00007fff365a163b -[FBSSerialQueue _performNextFromRunLoopSource] + 22
    48  CoreFoundation                      0x00007fff23bb2221 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    49  CoreFoundation                      0x00007fff23bb214c __CFRunLoopDoSource0 + 76
    50  CoreFoundation                      0x00007fff23bb1924 __CFRunLoopDoSources0 + 180
    51  CoreFoundation                      0x00007fff23bac62f __CFRunLoopRun + 1263
    52  CoreFoundation                      0x00007fff23babe16 CFRunLoopRunSpecific + 438
    53  GraphicsServices                    0x00007fff38438bb0 GSEventRunModal + 65
    54  UIKitCore                           0x00007fff4784fb68 UIApplicationMain + 1621
    55  ConfigExample                       0x0000000108a450a0 main + 112
    56  libdyld.dylib                       0x00007fff51a1dc25 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
morganchen12 commented 4 years ago

@dmandar can you investigate why the class is being loaded into the runtime despite it not having a definition?

@elena-krakhmalova in the meantime you can work around this bug by skipping the generated Protobuf classes when enumerating classes in the runtime.

elena-krakhmalova commented 4 years ago

@morganchen12 thanks for looking into the issue! Things are a bit more complicated, since I’m working on SDKs. Logging system of one of our SDKs enumerates classes at runtime. The second SDK includes the first SDK as submodule. User who integrated our second, bigger SDK, and also FirebaseRemoteConfig in his app complains about crash. We’ll have to make two releases until workaround will be reachable for user. I was considering possibility to add in user’s app AndroidConfigFetchProto class with empty implementation, without any properties or methods (serving just like a stub to prevent crash). I am not sure however, maybe there are some messages later at runtime to this class, when remote config is fetched, updated etc? Could you please confirm if this a possible workaround or not a good one? Could I recommend it to the user of our SDK? Thanks a lot in advance!

BennettSmith commented 4 years ago

I am experiencing this crash as well, when I enumerate all classes using the objc/runtime. I have tried the suggested workaround of skipping enumeration of the offending class, but that doesn't seem to work unless I'm missing something about how to skip them. Here is what I'm trying:

    int numberOfClasses = objc_getClassList(NULL, 0);
    Class *classList = (Class *)malloc(numberOfClasses * sizeof(Class));
    numberOfClasses = objc_getClassList(classList, numberOfClasses);

    for (int idx = 0; idx < numberOfClasses; idx++) {
        Class class = classList[idx];
        NSString *name = NSStringFromClass(class);
        NSLog(@"name = %@", name);
        if ([name compare:@"AndroidConfigFetchProto"] == NSOrderedSame) {
            NSLog(@"skipping");
        }
        else {
            // This will crash as of Nov 19, 2019 due to an issue in the Google Protobuf stuff
            // used by Firebase.  Refer to https://github.com/firebase/firebase-ios-sdk/issues/4334 for
            // details on the issue.  Basially, there is a forward declaration of a class named
            // AndroidConfigFetchProto in the FirebaseRemoteConfig library. When you use the Objective-C
            // runtime ability to enumerate classes it crashes because the class is not actually present.
            //
            // A suggested workaround is to skip enumeration of the generated Protobuf classes.
            if (class_getClassMethod(class, @selector(conformsToProtocol:)) && [class conformsToProtocol:protocol]) {
                if (class != [RMFModeTraitsImpl class]) {
                    id<RMFModeTraits> traitsClass = [[class alloc] init];
                    [modes addObject:traitsClass];
                }
            }
        }
    }

Unfortunately, it now crashes on this line:

        NSString *name = NSStringFromClass(class);

for the missing class.

What is the suggested way to work around this issue without being able to check the name of the offending class?

morganchen12 commented 4 years ago

@BennettSmith I haven't tested this solution, but you should be able to test for the missing class with

if (class != nil) {
    NSString *name = NSStringFromClass(class);
    // ...
}

Adding an empty stub implementation should work as well.

BennettSmith commented 4 years ago

Yeah, as I mention, when you try NSStringFromClass it causes the runtime crash too. So, it seems the Objective-C runtime is asserting that since the class doesn't exist, it won't even return the name of it. :-(

I'll give the stub approach a try.

What is the likelihood of this being resolve within the library? Is there a suggested earlier version of the pod that could be used to work around the issue?

morganchen12 commented 4 years ago

This will definitely be resolved in the library; missing implementations at runtime is a bug.

I'm not sure if earlier versions of RemoteConfig will fix this issue since that class has existed for a long time.

BennettSmith commented 4 years ago

Thanks for the additional info. Using the stub approach I am able to enumerate classes again.

Oddly, until the most recent pod update here our unit tests were all passing without an issue. We don't use RemoteConfig directly, but do use Firebase Analytics which I believe depends on RemoteConfig. I wonder if the internal inconsistency check and subsequent exception are something newly added by Apple in Xcode 11.2.1.

paulb777 commented 4 years ago

FirebaseAnalytics does not depend on FirebaseRemoteConfig. Check the Podfile.lock to see what causes the dependency.

elena-krakhmalova commented 4 years ago

Hi @BennettSmith it's interesting that with Xcode v11.2 (11B52) your code is not crashing at NSString *name = NSStringFromClass(class);

@morganchen12 thanks for your response! Just out of curiosity could you please describe in few words how is Android stuff relevant in iOS SDK? I can see in addition to AndroidConfigFetchProto class, for example hasAndroidId, androidId properties.

BennettSmith commented 4 years ago

FirebaseAnalytics does not depend on FirebaseRemoteConfig. Check the Podfile.lock to see what causes the dependency.

I misspoke - meant Firebase Performance, not Analytics.

morganchen12 commented 4 years ago

@elena-krakhmalova Android is not relevant in iOS, iirc when the service was originally written we put Android in some of the proto names and then re-used them on iOS.

elena-krakhmalova commented 4 years ago

Thanks @morganchen12 for your answer. I couldn’t find usage of AndroidConfigFetchProto *config property and I hope it’s not used on iOS.

dmandar commented 4 years ago

Investigating.

elena-krakhmalova commented 4 years ago

Hi @morganchen12 , @dmandar! any updates about this issue?

lguillermo commented 4 years ago

Having the exact same issue

elena-krakhmalova commented 4 years ago

@paulb777 Could you please tell me in which version of FirebaseRemoteConfig is this issue fixed? Is it already released or when is it planned to be released? Thanks a lot!

paulb777 commented 4 years ago

@elena-krakhmalova YW! It was released in Firebase 6.26.0. (You can see from the milestone field on this issue.)

elena-krakhmalova commented 4 years ago

@paulb777 if I'm using pod 'FirebaseRemoteConfig' in Podfile, I'm getting FirebaseRemoteConfig (4.4.11) installed. If using pod 'Firebase/RemoteConfig' in Podfile - getting Firebase 6.26.0 and FirebaseRemoteConfig 4.5.0 installed. So FirebaseRemoteConfig 4.5.0 is the last one and with fix, correct?

paulb777 commented 4 years ago

@elena-krakhmalova Correct. 4.5.0 has the fix. That should also show up if you do a pod update with Firebase/RemoteConfig from Firebase 6.26.0.

elena-krakhmalova commented 4 years ago

@paulb777 I see, thanks a lot!