Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions app/src/util_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,23 @@ static jobject GetParentLoader() {
return g_class_loaders->back();
}

// Convert a JNI class name (e.g. "java/lang/String"), as used by
// JNIEnv::FindClass(), to the binary name (e.g. "java.lang.String") expected by
// ClassLoader methods.
static std::string JniClassNameToBinaryName(const char* class_name) {
if (!class_name) return std::string();
std::string binary_name(class_name);
std::replace(binary_name.begin(), binary_name.end(), '/', '.');
return binary_name;
}
Comment on lines +303 to +308

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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;
}


// Try to find a class in the list of class loaders.
static jclass FindOrLoadClassFromLoaders(JNIEnv* env, const char* class_name) {
assert(g_class_loaders);
static const class_loader::Method kFindLoadClassMethods[] = {
class_loader::kFindLoadedClass, class_loader::kLoadClass};
jstring class_name_object = env->NewStringUTF(class_name);
jstring class_name_object =
env->NewStringUTF(JniClassNameToBinaryName(class_name).c_str());
jclass class_object = nullptr;
for (size_t i = 0; i < FIREBASE_ARRAYSIZE(kFindLoadClassMethods); ++i) {
for (auto it = g_class_loaders->begin();
Expand Down Expand Up @@ -1669,8 +1680,6 @@ jclass FindClassInFiles(
firebase::util::JniStringToString(env, cache_dir_path_jstring);

#if defined(FIREBASE_ANDROID_FOR_DESKTOP)
static const char kPathSeparator = '/';

jobject cache_uri =
env->CallObjectMethod(cache_dir, file::GetMethodId(file::kToUri));
CheckAndClearJniExceptions(env);
Expand Down Expand Up @@ -1698,11 +1707,9 @@ jclass FindClassInFiles(
url_path_array, GetParentLoader());
env->DeleteLocalRef(url_path_array);

std::string class_name_str(class_name);
std::replace(class_name_str.begin(), class_name_str.end(), kPathSeparator,
'.');
LogDebug("Load class %s (a.k.a. %s)", class_name_str.c_str(), class_name);
jstring class_name_object = env->NewStringUTF(class_name_str.c_str());
std::string binary_name = JniClassNameToBinaryName(class_name);
LogDebug("Load class %s (a.k.a. %s)", binary_name.c_str(), class_name);
jstring class_name_object = env->NewStringUTF(binary_name.c_str());
jclass loaded_class = static_cast<jclass>(env->CallObjectMethod(
class_loader_obj,
url_class_loader::GetMethodId(url_class_loader::kLoadClass),
Expand Down Expand Up @@ -1745,8 +1752,9 @@ jclass FindClassInFiles(
env->DeleteLocalRef(code_cache_dir_path);
env->DeleteLocalRef(dex_path_string);

LogDebug("Load class %s", class_name);
jstring class_name_object = env->NewStringUTF(class_name);
std::string binary_name = JniClassNameToBinaryName(class_name);
LogDebug("Load class %s (a.k.a. %s)", binary_name.c_str(), class_name);
jstring class_name_object = env->NewStringUTF(binary_name.c_str());
jclass loaded_class = static_cast<jclass>(env->CallObjectMethod(
class_loader_obj,
dex_class_loader::GetMethodId(dex_class_loader::kLoadClass),
Expand Down
17 changes: 11 additions & 6 deletions database/src/common/database.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,20 @@ void Database::DeleteInternal() {

if (!internal_) return;

// An instance whose initialization failed was never registered with the
// App's CleanupNotifier or added to g_databases, and app() may be null.
if (!internal_->initialized()) {
delete internal_;
internal_ = nullptr;
return;
}

App* my_app = app();
std::string url = internal_->constructor_url();

// Only need to unregister if internal_ is initialized
if (internal_->initialized()) {
CleanupNotifier* app_notifier = CleanupNotifier::FindByOwner(my_app);
assert(app_notifier);
app_notifier->UnregisterObject(this);
}
CleanupNotifier* app_notifier = CleanupNotifier::FindByOwner(my_app);
assert(app_notifier);
app_notifier->UnregisterObject(this);

{
MutexLock db_ref_lock(internal::g_database_reference_constructor_mutex);
Expand Down
8 changes: 8 additions & 0 deletions functions/src/common/functions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ void Functions::DeleteInternal() {

if (!internal_) return;

// An instance whose initialization failed was never registered with the
// App's CleanupNotifier or added to g_functions, and app() may be null.
if (!internal_->initialized()) {
delete internal_;
internal_ = nullptr;
return;
}

CleanupNotifier* app_notifier = CleanupNotifier::FindByOwner(app());
assert(app_notifier);
app_notifier->UnregisterObject(this);
Expand Down
6 changes: 6 additions & 0 deletions release_build_files/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,12 @@ code.
## Release Notes
### Upcoming
- Changes
- Database, Functions, Storage (Android): Fixed a crash in `GetInstance()`
when initialization fails. It now returns `nullptr` and reports
`kInitResultFailedMissingDependency`.
- General (Android): Fixed loading of embedded Java classes when the app
runs under a custom class loader setup, such as being hosted in another
app's process. (#1936)
- General (Android): Fixed a potential buffer overflow in `LogException` when
formatting long JNI exception messages.

Expand Down
8 changes: 8 additions & 0 deletions storage/src/common/storage.cc
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ void Storage::DeleteInternal() {
MutexLock lock(g_storages_lock);
if (!internal_) return;

// An instance whose initialization failed was never registered with the
// App's CleanupNotifier or added to g_storages, and app() may be null.
if (!internal_->initialized()) {
delete internal_;
internal_ = nullptr;
return;
}

CleanupNotifier* app_notifier = CleanupNotifier::FindByOwner(app());
assert(app_notifier);
app_notifier->UnregisterObject(this);
Expand Down
Loading