Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
elena-krakhmalova opened this issue Nov 17, 2019 · 22 comments · Fixed by #5639
Assignees
Milestone

Comments

@elena-krakhmalova
Copy link

elena-krakhmalova commented Nov 17, 2019

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

Yes, seems to me in the right place.

[REQUIRED] Step 2: Describe your environment

Environment

  • Xcode version: Version 11.2 (11B52)
  • Firebase SDK version: 6.13.0
  • Firebase Component: RemoteConfig
  • Component version: 4.4.3, 4.4.5

[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

This comment has been minimized.

@morganchen12
Copy link
Contributor

@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 morganchen12 removed their assignment Nov 20, 2019
@morganchen12
Copy link
Contributor

@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
Copy link
Author

@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
Copy link

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
Copy link
Contributor

morganchen12 commented Nov 21, 2019

@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
Copy link

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
Copy link
Contributor

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
Copy link

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
Copy link
Member

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

@elena-krakhmalova
Copy link
Author

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
Copy link

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

I misspoke - meant Firebase Performance, not Analytics.

@morganchen12
Copy link
Contributor

@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
Copy link
Author

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

@dmandar
Copy link
Contributor

dmandar commented Nov 25, 2019

Investigating.

@dmandar dmandar closed this as completed Nov 25, 2019
@dmandar dmandar reopened this Nov 25, 2019
@elena-krakhmalova
Copy link
Author

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

@lguillermo
Copy link

Having the exact same issue

@paulb777 paulb777 added this to the 6.26.0 - M72 milestone May 19, 2020
@elena-krakhmalova
Copy link
Author

@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
Copy link
Member

paulb777 commented Jun 4, 2020

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

@elena-krakhmalova
Copy link
Author

@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
Copy link
Member

paulb777 commented Jun 4, 2020

@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
Copy link
Author

@paulb777 I see, thanks a lot!

@firebase firebase locked and limited conversation to collaborators Jun 19, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants