Repository navigation
Database, Functions, Storage (Android): Fix crash on failed init and embedded class lookup - #1947
AlekseyAzarov wants to merge 3 commits into
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
There was a problem hiding this comment.
Code Review
This pull request addresses initialization and class loading issues on Android. Specifically, it prevents crashes in Database, Functions, and Storage when initialization fails by ensuring uninitialized instances are safely deleted without accessing potentially null app pointers. Additionally, it resolves class loading issues under custom class loaders by introducing a helper function to convert JNI class names to binary names. The review feedback recommends adding a null check to this new helper function to avoid undefined behavior and potential crashes when handling null class name pointers.
| static std::string JniClassNameToBinaryName(const char* class_name) { | ||
| std::string binary_name(class_name); | ||
| std::replace(binary_name.begin(), binary_name.end(), '/', '.'); | ||
| return binary_name; | ||
| } |
There was a problem hiding this comment.
Constructing a std::string from a nullptr const char* is undefined behavior in C++ and will cause a crash. To ensure robust defensive programming, we should add a null check for class_name before initializing the std::string.
static std::string JniClassNameToBinaryName(const char* class_name) {
if (!class_name) {
return "";
}
std::string binary_name(class_name);
std::replace(binary_name.begin(), binary_name.end(), '/', '.');
return binary_name;
}Fixes firebase#1936. Database/Functions/Storage: DeleteInternal() no longer crashes when the instance failed to initialize. GetInstance() deletes such an instance, and on Android its internal object has a null App, so DeleteInternal() dereferenced a null App* (e.g. in MakeKey() via app->name()) instead of letting GetInstance() return nullptr with kInitResultFailedMissingDependency. An uninitialized instance was never registered with the App's CleanupNotifier or added to the instance cache, so DeleteInternal() now just deletes the internal object and returns. util_android: pass binary class names ("a.b.C") rather than JNI names ("a/b/C") to ClassLoader.findLoadedClass()/loadClass(). The Android branch of FindClassInFiles() and FindOrLoadClassFromLoaders() passed JNI names, which ART tolerates in a standalone app but which fail when the app runs under a custom class-loader setup (e.g. hosted in another app's process), so the embedded classes such as CppTransactionHandler could not be loaded. The conversion is now shared by both branches of FindClassInFiles() via JniClassNameToBinaryName().
5bc71b5 to
3d5874a
Compare
Description
Fixes #1936. Two related Android defects: a failed embedded class lookup made
Database::GetInstance()fail, and the failure path then crashed with SIGSEGVinstead of returning an error.
1. Crash in
DeleteInternal()after a failed initialization (Database, Functions, Storage)When
*Internalinitialization fails,GetInstance()deletes the new instanceand should return
nullptrwithkInitResultFailedMissingDependency. OnAndroid the internal object of a failed instance has a null
App, butDeleteInternal()still used it: Database calledMakeKey()→app->name(), and Functions/Storage calledCleanupNotifier::FindByOwner(app())andapp()->options(). A failedinstance is never registered with the App's
CleanupNotifieror added to theinstance cache, so
DeleteInternal()now just deletes the internal objectand returns in that case. Desktop behavior is unchanged: there
initialized()is always true.
2. JNI class names passed to
ClassLoader(app/src/util_android.cc)ClassLoader.loadClass()andfindLoadedClass()expect binary names(
a.b.C), but the Android branch ofFindClassInFiles()andFindOrLoadClassFromLoaders()passed JNI names (a/b/C). ART accepts thesein a standalone app, but the lookup fails under a custom class-loader setup,
as in the reporter's case where the app is hosted in another app's process.
The desktop branch of
FindClassInFiles()already converted the name. Theconversion now lives in a shared helper,
JniClassNameToBinaryName(), whichboth branches of
FindClassInFiles()andFindOrLoadClassFromLoaders()use.The first lookup through
JNIEnv::FindClass()still uses the JNI name, sothere is no change where lookup already succeeds.
Testing
on Ubuntu, Windows and macOS (run on 79b9e21, the code commit; the later
commit only adds release notes).
on Ubuntu, macOS and Windows, including the unit tests where the
workflow runs them and
local-integration-tests-linux-openssl.(
-fsyntax-only -Wall), with no errors or warnings.Checksworkflow uses on ubuntu-22.04. It reported no changes.
a maintainer please add
tests-requested: quick?because embedded class loading would have to fail. The reporter of [Database][Android] SIGSEGV in Database::DeleteInternal when embedded bridge-class init fails; FindClassInFiles passes JNI-form names to loadClass on Android #1936
offered to test a patched build in their hosted-process setup.
Type of Change
Place an
xthe applicable box:Fixes #1936
Notes
Release Notessection ofrelease_build_files/readme.md.🤖 Generated with Claude Code