Skip to content

[Database][Android] SIGSEGV in Database::DeleteInternal when embedded bridge-class init fails; FindClassInFiles passes JNI-form names to loadClass on Android #1936

Open

Description

@Tb1281

[Database][Android] SIGSEGV in Database::DeleteInternal when embedded bridge-class init fails; FindClassInFiles passes JNI-form names to loadClass on Android

SDK: Firebase C++ SDK 13.12.0, Realtime Database (libFirebaseCppApp-13_12_0.so), arm64 Android.

Two related defects, one crash. Reporting both together because the second turns the first from "error return" into "process death".

1. Init failure crashes instead of returning an error (the SIGSEGV)

When Database::GetInstance cannot finish DatabaseInternal init (in our case: embedded Java bridge classes fail to resolve, see §2), the code path ends in delete database → Database::DeleteInternal, which dereferences a null App* (MakeKey(app=null) → app->name()).

Tombstone (deterministic, every run once the init fails):

signal 11 (SIGSEGV), SEGV_MAPERR, fault addr 0x8 (null+8 read)
#00 libFirebaseCppApp-13_12_0.so  firebase::database::Database::DeleteInternal()+192
#01 libFirebaseCppApp-13_12_0.so  firebase::database::Database::GetInstance(...)+792
#02 libFirebaseCppApp-13_12_0.so  Firebase_Database_CSharp_InternalFirebaseDatabase_GetInstanceInternal

Expected: init failure returns an error / null instance. Actual: SIGSEGV. This half is environment-independent — any init failure ends in a crash rather than a diagnosable error, which is also what made §2 so hard to root-cause (no error, just death).

2. Android branch of FindClassInFiles skips the slash→dot conversion the desktop branch does

app/src/util_android.cc, FindClassInFiles (verified on current main):

  • Desktop branch converts before calling loadClass:
    std::string class_name_str(class_name);
    std::replace(class_name_str.begin(), class_name_str.end(), kPathSeparator, '.');
    with LogDebug("Load class %s (a.k.a. %s)", ...).
  • Android branch passes the raw JNI-form name straight into DexClassLoader.loadClass:
    LogDebug("Load class %s", class_name);
    jstring class_name_object = env->NewStringUTF(class_name);
    ... kLoadClass ...

ClassLoader.loadClass takes binary names (dotted). On typical standalone apps ART tolerates the slashed form so nobody notices; under a custom class-loader setup the lookup fails. Preceding log line in our runs:

E firebase: Java class com/google/firebase/database/internal/cpp/CppTransactionHandler not found.

Suggested fix: the same 3-line std::replace in the Android branch — makes both branches consistent, zero behavior change where lookup already succeeds.

Environment (disclosed so this isn't mis-triaged)

The game runs hosted in another app's process (launcher creates the game context via createPackageContext and hosts its activity). That is why the class-loader topology differs from standalone and why §2 bites here first. But §1 needs no hosted setup to matter: any GetInstance init failure on any app crashes today. And §2 is a contract violation that only happens to work standalone.

Happy to attach the full tombstone + logcat and to test a patched build. Thanks for the SDK.

Activity

  1. AlekseyAzarov commented on Oct 24, 2026

    @AlekseyAzarov

    Hi @Tb1281, thanks for the detailed report. It pointed straight at both problems. I've opened #1947, which fixes both:

    1. Crash on failed init: Database::DeleteInternal() no longer dereferences the null App* when initialization fails, so GetInstance() fails with an error instead of a SIGSEGV. Functions and Storage had the same bug and are fixed too.
    2. Class lookup: the Android branch of FindClassInFiles() now passes the binary name (a.b.C) to ClassLoader.loadClass(), as you suggested. FindOrLoadClassFromLoaders() had the same problem and is fixed too; FindClass() tries it before FindClassInFiles().

    You offered to test a patched build. Since you're on Unity, that means building the Firebase Unity SDK against the patched C++ sources. The simplest way is to apply the fix to 13.8.0, the version you already use. The build output then replaces your current Firebase files directly, with no upgrade. (The PR targets main, which is 14.x, and 14.0.0 has breaking changes such as Android minSdkVersion 24.) I checked that the fix applies cleanly on top of v13.12.0 and that the changed files compile there, so it should work on the version you're using.

    Build a patched 13.8.0 Unity SDK (Android)

    Note: I put these instructions together with Claude, based on the firebase-unity-sdk sources and its CI configuration. I haven't gone through them myself, so some steps may need adjusting.

    Prerequisites (as for the official build, see the firebase-unity-sdk README):

    • CMake, and Python 3 with absl-py
    • SWIG 4 or newer
    • Unity with Android Build Support
    • Android NDK r23b (the version the Unity SDK CI uses), with ANDROID_NDK_HOME and NDK_ROOT pointing to it
    • JDK 11, set in JAVA_HOME

    The upstream docs recommend macOS for the Android build, but Linux and Windows are supported.

    1. Clone both repos side by side at 13.12.0 and apply the fix:

    mkdir firebase-build && cd firebase-build
    
    # C++ SDK 13.12.0 + the code changes from #1947
    git clone https://github.com/firebase/firebase-cpp-sdk.git
    cd firebase-cpp-sdk
    git checkout v13.12.0
    git fetch origin pull/1947/head:pr-1947
    git diff $(git merge-base pr-1947 origin/main) pr-1947 -- app database functions storage | git apply
    cd ..
    
    # Unity SDK 13.12.0
    git clone --recursive -b v13.12.0 https://github.com/firebase/firebase-unity-sdk.git

    The C++ checkout must be named firebase-cpp-sdk and sit next to firebase-unity-sdk. build_zips.py picks it up from there automatically. Otherwise, set FIREBASE_CPP_SDK_DIR to its path.

    2. Build the Android zip. List every Firebase product your game uses: they are all linked into the single libFirebaseCppApp-13_12_0.so, so a product you leave out would be missing at runtime.

    cd firebase-unity-sdk
    pip install -r scripts/gha/requirements.txt
    python scripts/build_scripts/build_zips.py --platform=android --apis=database,auth
    # Adjust --apis to your products. Add --unity_root=<path to Unity>
    # if Unity isn't installed in the default location.

    The result is android_unity/firebase_unity-13.12.0-Android.zip.

    3. Put it into your project. The Android libraries are packaged as .srcaar files under Firebase/m2repository/com/google/firebase/, both in the zip and in your project's Assets/. Back up your project, replace Assets/Firebase/m2repository with the zip's Firebase/m2repository, then run Assets → External Dependency Manager → Android Resolver → Force Resolve and rebuild the app.

    What to look for

    • The SIGSEGV in Database::DeleteInternal should be gone in all cases. If initialization still fails for some other reason, GetInstance now reports an error instead of crashing, and logcat should show the real cause.
    • With the class-name fix, CppTransactionHandler should load in your hosted-process setup, and Database should initialize normally. To see the class loading in logcat, set Firebase.FirebaseApp.LogLevel = Firebase.LogLevel.Debug; before using Firebase and look for lines like Load class com.google.firebase.database.internal.cpp.CppTransactionHandler (a.k.a. com/google/...).

    It would help a lot if you could post the result in this thread. If anything still fails, please attach the logcat and the tombstone.

    Alternative: build against the PR itself (14.x) with GitHub Actions

    If you'd rather not build locally, you can fork firebase/firebase-unity-sdk and run the Firebase Unity SDK build workflow on your fork (Actions → Run workflow) with firebase_cpp_sdk_version set to refs/pull/1947/head, platforms set to Android, and apis set to your products. This builds against the PR branch, so you get a 14.x SDK and would need to upgrade your project. I haven't tested this route myself.

  2. Tb1281 commented on Oct 24, 2026

    @Tb1281
    Author

    Hi Aleksey, thank you for confirming both problems and for opening #1947 — great to see the analysis validated, and impressively fast.

    One clarification about testing, though: I am not the game's developer. I work on a third-party Android launcher/modding setup that hosts the game process, so I don't have the game's Unity project and can't rebuild it with a patched Firebase SDK. The embedded libFirebaseCppApp-13_12_0.so in the game can only be replaced by the game's developer shipping an SDK update, so I'm afraid I can't test the patched build the way you described.

    What I can say from my side: for our launcher's own embedded bridge jar, we already worked around the class-order symptom by defining classes in supertype-first order, which resolved the CppChildEventListener failures in our hosted-process setup. But the null-deref crash on failed init lives in the game's own SDK, so only an SDK update fixes that path for existing games.

    One question: is there any plan to backport this fix to a 13.x release, or will it only land in 14.x? A 13.x backport would matter a lot for games still on 13.12.0 — otherwise they would have to take the 14.0.0 breaking changes (e.g. minSdk 24) to get the fix.

    Thanks again for the quick turnaround.

  3. AlekseyAzarov commented on Oct 24, 2026

    @AlekseyAzarov

    Thanks for clarifying, and for explaining the workaround on your side.

    As an external contributor I can't decide whether this gets backported to 13.x. That's up to the Firebase maintainers. I'll leave that question to them here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions