firebase-cpp-sdk: [Bug] High CPU utilisation after authentication when running in sandbox on desktop (macOS)

[REQUIRED] Please fill in the following fields:

  • Pre-built SDK from the website or open-source from this repo: Pre-built
  • Firebase C++ SDK version: 9.3
  • Problematic Firebase Component: Auth (Auth, Database, etc.)
  • Other Firebase Components in use: Firestore (Auth, Database, etc.)
  • Platform you are using the C++ SDK on: Mac ARM64 & x86 (Mac, Windows, or Linux)
  • Platform you are targeting: Desktop (iOS, Android, and/or desktop)

[REQUIRED] Please describe the issue here:

When running the Firebase C++ SDK in a sandboxed app on macOS 12.5, high CPU utilisation is observed after authentication succeeds. Without sandboxing enabled, the issue does not occur.

Steps to reproduce:

Follow these steps to set up a test project that reproduces the issue. Or use the attached test project below.

  1. Include Firebase Auth in a C++ or Objective-C++ class and implement an email + password authentication step.
  2. Create an Xcode project with the template macOS → Application → App (Interface XIB, in order to select language Objective-C). Add your C++/Objective-C++ class to this project and the app target.
  3. Under project → targets → $appname, add firebase_auth.framework (from fb_cpp_sdk/frameworks/darwin/universal/ in the pre-built C++ SDK folder) to the section “Frameworks, Libraries and Embedded Content”, with the option Embed & Sign selected. Also add the other macOS dependencies here (CoreFoundation, Foundation, GSS, Kerberos, libpthread, Security and SystemConfiguration. Keep the standard value for embedding, Do not Embed).
  4. Check that the frameworks in fb_cpp_sdk/frameworks/darwin/universal/ either contain all headers (four header files are missing from the v9.3 download in fb_cpp_sdk/frameworks/darwin/universal/firebase.framework/Headers/) or manually add the project header search path for fb_cpp_sdk/include/.
  5. The project template contains the file $appname.entitlements. Make sure that the value for key “Sandboxing enabled” in this property list file is set to YES, in order to create a sandboxed app. Additionally, set both the com.apple.security.network.client and server values to YES, in order to allow network access.
  6. Build the app and run it, then sign in using email + password.
  7. As soon as the authentication succeeds, four Firebase worker threads keep running at full CPU utilisation. This can be checked in the debug navigator of Xcode when selecting CPU (displays about 400% usage continuously).

Expected outcome: The worker threads should use minimal CPU resources and sleep whenever possible.

  1. (Optionally) Go back to the entitlements file and set “Sandboxing enabled” to NO, in order to deactivate sandboxing. The issue should no longer occur after clean build products and rebuild in Xcode, when running the app again and signing in.

Have you been able to reproduce this issue with just the Firebase C++ quickstarts ? What’s the issue repro rate? (eg 100%, 1/5 etc)

Yes, happens with any sandboxed Mac app build (100%).

What happened? How can we make the problem occur? This could be a description, log/console output, etc.

As soon as the authentication succeeds, four Firebase worker keep running at full CPU utilisation. This is mostly due to firebase::scheduler::Scheduler::WorkerThreadRoutine. This can be verified using Instruments, when sampling the running app.

Expected outcome: The worker threads should use minimal CPU resources and sleep whenever possible.

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 8
  • Comments: 18 (6 by maintainers)

Most upvoted comments

Hi @infiniteloopunroll,

We thought adding a field to AppOptions but in theory, if you initialize the default app and then later create a custom app with AppOptions, it would already be too late to configure the semaphore names. Or, alternatively, subsequent calls to create an App with different AppOptions wouldn’t affect the name of the semaphores already created.

We’d rather have a solution that doesn’t make it seem like the configuration can vary across instances of Firebase Apps.

That being said, we’re also thinking about adding an alternative global function in the firebase namespace for those who wish to configure this value programmatically.

The Info.plist solution should be part of our next release since that solution has already been merged to main. The API solution might come a bit later.

I was able to reproduce this on an intel mac as well. Thanks for the test case!

Hi @infiniteloopunroll,

I’ve been looking into this issue and I believe I’ve found the problem.

When macOS Sandbox mode is enabled, macOS requires that semaphores have a name that is prefixed by the App’s Group Name. If the semaphore’s name doesn’t match this convention then its creation fails.

As an additional issue, we attempted to detect semaphore creation errors by comparing the semaphore handle to nil. But in the the case of macOS, a a semaphore creation error returns SEM_FAILED which is 0xFFFFFFFFFFFFFFFF, not nil. We therefore didn’t detect these creation errors and started to use the semaphore anyway. Subsequent sem_wait calls would fail, too, which is why we saw tight looping and high CPU usage in our worker threads. They were just constantly spinning.

Unfortunately there’s no official way for the SDK to query the app’s group name at runtime, so we can’t automatically mangle the semaphore names.

Instead I’m thinking that the Firebase SDK could use a property in your Info.plist, something that you could add like FBAppGroupEntitlementName for instance. If that property is present then the SDK would use it to prefix the semaphore names. This way you could plumb the App Group name to the SDK, and the semaphores would be created properly.

Would this be a good way to solve the problem for you?

Also, it turns out the network sandbox entitlements com.apple.security.network.client and ...server values aren’t related to this problem. That is, I can reproduce this issue without configuring them. If someone encounters an issue when these are set then please let me know!

I can reproduce the same issue on Intel Mac running macOS 12.6.

May I understand why you need to run macOS application in this mode?

All software distributed in the Mac App Store must be sandboxed per Apple guidelines, and additionally there are some Apple APIs that can only be accessed if the app is running sandboxed (even for local test builds). This is why these days nearly all macOS software is sandboxed.

Oh, this is incredibly useful. Thank you!

Based on your information, I think this happens when IdTokenRefreshThread is waiting for token refresh request. https://github.com/firebase/firebase-cpp-sdk/blob/3f79c788eb396b499e98f6f9192cdd9b4020cf00/auth/src/desktop/auth_desktop.cc#L694

This refresh request will need internet access in order to get the token from the auth backend. I assume you did enabled Outgoing connection (client) in the sandboxing setting, correct? https://developer.apple.com/documentation/xcode/configuring-the-macos-app-sandbox/

I have a feeling this is something weird going on with this Sandboxing mode. May I understand why you need to run macOS application in this mode?