/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "nativeloader" #include "nativeloader/native_loader.h" #include #include #include #include #include #include #include #include #include #include "android-base/file.h" #include "android-base/macros.h" #include #include "android-base/strings.h" #include "android-base/thread_annotations.h" #include "base/macros.h" #include "nativebridge/native_bridge.h" #include "nativehelper/scoped_utf_chars.h" #include "public_libraries.h" #ifdef ART_TARGET_ANDROID #include "android-modules-utils/sdk_level.h" #include "android/api-level.h" #include "library_namespaces.h" #include "log/log.h" #include "nativeloader/dlext_namespaces.h" #endif namespace android { namespace { #if defined(ART_TARGET_ANDROID) using ::android::base::Result; using ::android::nativeloader::LibraryNamespaces; // NATIVELOADER_DEFAULT_NAMESPACE_LIBS is an environment variable that can be // used to list extra libraries (separated by ":") that libnativeloader will // load from the default namespace. The libraries must be listed without paths, // and then LD_LIBRARY_PATH is typically set to the directories to load them // from. The libraries will be available in all classloader namespaces, and also // in the fallback namespace used when no classloader is given. // // kNativeloaderExtraLibs is the name of that fallback namespace. // // NATIVELOADER_DEFAULT_NAMESPACE_LIBS is intended to be used for testing only, // and in particular in the ART run tests that are executed through dalvikvm in // the APEX. In that case the default namespace links to the ART namespace // (com_android_art) for all libraries, which means this can be used to load // test libraries that depend on ART internal libraries. constexpr const char* kNativeloaderExtraLibs = "nativeloader-extra-libs"; std::mutex g_namespaces_mutex; LibraryNamespaces* g_namespaces GUARDED_BY(g_namespaces_mutex) = new LibraryNamespaces; NativeLoaderNamespace* g_nativeloader_extra_libs_namespace GUARDED_BY(g_namespaces_mutex) = nullptr; std::optional FindApexNamespace(const char* caller_location) { std::optional name = nativeloader::FindApexNamespaceName(caller_location); if (name.has_value()) { // Native Bridge is never used for APEXes. Result ns = NativeLoaderNamespace::GetExportedNamespace(name.value(), /*is_bridged=*/false); LOG_ALWAYS_FATAL_IF(!ns.ok(), "Error finding ns %s for APEX location %s: %s", name.value().c_str(), caller_location, ns.error().message().c_str()); return ns.value(); } return std::nullopt; } Result GetNamespaceForApiDomain(nativeloader::ApiDomain api_domain, bool is_bridged) { switch (api_domain) { case nativeloader::API_DOMAIN_VENDOR: return NativeLoaderNamespace::GetExportedNamespace(nativeloader::kVendorNamespaceName, is_bridged); case nativeloader::API_DOMAIN_PRODUCT: return NativeLoaderNamespace::GetExportedNamespace(nativeloader::kProductNamespaceName, is_bridged); case nativeloader::API_DOMAIN_SYSTEM: return NativeLoaderNamespace::GetSystemNamespace(is_bridged); default: LOG_FATAL("Invalid API domain %d", api_domain); UNREACHABLE(); } } Result CreateNativeloaderDefaultNamespaceLibsLink(NativeLoaderNamespace& ns) REQUIRES(g_namespaces_mutex) { const char* links = getenv("NATIVELOADER_DEFAULT_NAMESPACE_LIBS"); if (links == nullptr || *links == 0) { return {}; } // Pass nullptr to Link() to create a link to the default namespace without // requiring it to be visible. return ns.Link(nullptr, links); } Result GetNativeloaderExtraLibsNamespace() REQUIRES(g_namespaces_mutex) { if (g_nativeloader_extra_libs_namespace != nullptr) { return g_nativeloader_extra_libs_namespace; } Result ns = NativeLoaderNamespace::Create(kNativeloaderExtraLibs, /*search_paths=*/"", /*permitted_paths=*/"", /*parent=*/nullptr, /*is_shared=*/false, /*is_exempt_list_enabled=*/false, /*also_used_as_anonymous=*/false); if (!ns.ok()) { return ns.error(); } g_nativeloader_extra_libs_namespace = new NativeLoaderNamespace(std::move(ns.value())); Result linked = CreateNativeloaderDefaultNamespaceLibsLink(*g_nativeloader_extra_libs_namespace); if (!linked.ok()) { return linked.error(); } return g_nativeloader_extra_libs_namespace; } // If the given path matches a library in NATIVELOADER_DEFAULT_NAMESPACE_LIBS // then load it in the nativeloader-extra-libs namespace, otherwise return // nullptr without error. Result TryLoadNativeloaderExtraLib(const char* path) { const char* links = getenv("NATIVELOADER_DEFAULT_NAMESPACE_LIBS"); if (links == nullptr || *links == 0) { return nullptr; } std::vector lib_list = base::Split(links, ":"); if (std::find(lib_list.begin(), lib_list.end(), path) == lib_list.end()) { return nullptr; } std::lock_guard guard(g_namespaces_mutex); Result ns = GetNativeloaderExtraLibsNamespace(); if (!ns.ok()) { return ns.error(); } Result res = ns.value()->Load(path); ALOGD("Load %s using ns %s from NATIVELOADER_DEFAULT_NAMESPACE_LIBS match: %s", path, ns.value()->name().c_str(), res.ok() ? "ok" : res.error().message().c_str()); return res; } Result CreateClassLoaderNamespaceLocked(JNIEnv* env, int32_t target_sdk_version, jobject class_loader, nativeloader::ApiDomain api_domain, bool is_shared, const std::string& dex_path, jstring library_path_j, jstring permitted_path_j, jstring uses_library_list_j) REQUIRES(g_namespaces_mutex) { Result ns = g_namespaces->Create(env, target_sdk_version, class_loader, api_domain, is_shared, dex_path, library_path_j, permitted_path_j, uses_library_list_j); if (!ns.ok()) { return ns; } Result linked = CreateNativeloaderDefaultNamespaceLibsLink(*ns.value()); if (!linked.ok()) { return linked.error(); } return ns; } #endif // ART_TARGET_ANDROID } // namespace void InitializeNativeLoader() { #if defined(ART_TARGET_ANDROID) std::lock_guard guard(g_namespaces_mutex); g_namespaces->Initialize(); #endif } void ResetNativeLoader() { #if defined(ART_TARGET_ANDROID) std::lock_guard guard(g_namespaces_mutex); g_namespaces->Reset(); delete g_nativeloader_extra_libs_namespace; g_nativeloader_extra_libs_namespace = nullptr; #endif } // dex_path_j may be a ':'-separated list of paths, e.g. when creating a shared // library loader - cf. mCodePaths in android.content.pm.SharedLibraryInfo. jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader, bool is_shared, jstring dex_path_j, jstring library_path_j, jstring permitted_path_j, jstring uses_library_list_j) { #if defined(ART_TARGET_ANDROID) std::string dex_path; if (dex_path_j != nullptr) { ScopedUtfChars dex_path_chars(env, dex_path_j); dex_path = dex_path_chars.c_str(); } Result api_domain = nativeloader::GetApiDomainFromPathList(dex_path); if (!api_domain.ok()) { return env->NewStringUTF(api_domain.error().message().c_str()); } std::lock_guard guard(g_namespaces_mutex); Result ns = CreateClassLoaderNamespaceLocked(env, target_sdk_version, class_loader, api_domain.value(), is_shared, dex_path, library_path_j, permitted_path_j, uses_library_list_j); if (!ns.ok()) { return env->NewStringUTF(ns.error().message().c_str()); } #else UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path_j, library_path_j, permitted_path_j, uses_library_list_j); #endif return nullptr; } #if defined(ART_TARGET_ANDROID) static bool ShouldBypassLoadingForB349878424() { struct stat st; if (stat("/system/lib64/libsobridge.so", &st) != 0 && stat("/system/lib64/libwalkstack.so", &st) != 0) { return false; } std::string property = android::base::GetProperty("ro.product.build.fingerprint", ""); return android_get_device_api_level() == 33 && (property.starts_with("Xiaomi") || property.starts_with("Redmi") || property.starts_with("POCO")); } #endif void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader, const char* caller_location, jstring library_path_j, bool* needs_native_bridge, char** error_msg) { #if defined(ART_TARGET_ANDROID) if (class_loader == nullptr) { // class_loader is null only for the boot class loader (see // IsBootClassLoader call in JavaVMExt::LoadNativeLibrary), i.e. the caller // is in the boot classpath. *needs_native_bridge = false; if (caller_location != nullptr) { std::optional ns = FindApexNamespace(caller_location); if (ns.has_value()) { const android_dlextinfo dlextinfo = { .flags = ANDROID_DLEXT_USE_NAMESPACE, .library_namespace = ns.value().ToRawAndroidNamespace(), }; void* handle = android_dlopen_ext(path, RTLD_NOW, &dlextinfo); char* dlerror_msg = handle == nullptr ? strdup(dlerror()) : nullptr; ALOGD("Load %s using APEX ns %s for caller %s: %s", path, ns.value().name().c_str(), caller_location, dlerror_msg == nullptr ? "ok" : dlerror_msg); if (dlerror_msg != nullptr) { *error_msg = dlerror_msg; } return handle; } } // Check if the library is in NATIVELOADER_DEFAULT_NAMESPACE_LIBS and should // be loaded from the kNativeloaderExtraLibs namespace. { Result handle = TryLoadNativeloaderExtraLib(path); if (!handle.ok()) { *error_msg = strdup(handle.error().message().c_str()); return nullptr; } if (handle.value() != nullptr) { return handle.value(); } } // Handle issue b/349878424. static bool bypass_loading_for_b349878424 = ShouldBypassLoadingForB349878424(); if (bypass_loading_for_b349878424 && (strcmp("libsobridge.so", path) == 0 || strcmp("libwalkstack.so", path) == 0)) { // Load a different library to pretend the loading was successful. This // allows the device to boot. ALOGD("Loading libbase.so instead of %s due to b/349878424", path); path = "libbase.so"; } // Fall back to the system namespace. This happens for preloaded JNI // libraries in the zygote. void* handle = OpenSystemLibrary(path, RTLD_NOW); char* dlerror_msg = handle == nullptr ? strdup(dlerror()) : nullptr; ALOGD("Load %s using system ns (caller=%s): %s", path, caller_location == nullptr ? "" : caller_location, dlerror_msg == nullptr ? "ok" : dlerror_msg); if (dlerror_msg != nullptr) { *error_msg = dlerror_msg; } return handle; } // If the caller is in any of the system image partitions and the library is // in the same partition then load it without regards to public library // restrictions. This is only done if the library is specified by an absolute // path, so we don't affect the lookup process for libraries specified by name // only. if (caller_location != nullptr && // Apps in the partition may have their own native libraries which should // be loaded with the app's classloader namespace, so only do this for // libraries in the partition-wide lib(64) directories. nativeloader::IsPartitionNativeLibPath(path) && // Don't do this if the system image is older than V, to avoid any compat // issues with apps and shared libs in them. android::modules::sdklevel::IsAtLeastV()) { nativeloader::ApiDomain caller_api_domain = nativeloader::GetApiDomainFromPath(caller_location); if (caller_api_domain != nativeloader::API_DOMAIN_DEFAULT) { nativeloader::ApiDomain library_api_domain = nativeloader::GetApiDomainFromPath(path); if (library_api_domain == caller_api_domain) { bool is_bridged = false; if (library_path_j != nullptr) { ScopedUtfChars library_path_utf_chars(env, library_path_j); if (library_path_utf_chars[0] != '\0') { is_bridged = NativeBridgeIsPathSupported(library_path_utf_chars.c_str()); } } Result ns = GetNamespaceForApiDomain(caller_api_domain, is_bridged); if (!ns.ok()) { ALOGD("Failed to find ns for caller %s in API domain %d to load %s (is_bridged=%b): %s", caller_location, caller_api_domain, path, is_bridged, ns.error().message().c_str()); *error_msg = strdup(ns.error().message().c_str()); return nullptr; } *needs_native_bridge = ns.value().IsBridged(); Result handle = ns.value().Load(path); ALOGD("Load %s using ns %s for caller %s in same partition (is_bridged=%b): %s", path, ns.value().name().c_str(), caller_location, is_bridged, handle.ok() ? "ok" : handle.error().message().c_str()); if (!handle.ok()) { *error_msg = strdup(handle.error().message().c_str()); return nullptr; } return handle.value(); } } } NativeLoaderNamespace* ns; const char* ns_descr; { std::lock_guard guard(g_namespaces_mutex); ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader); ns_descr = "class loader"; if (ns == nullptr) { // This is the case where the classloader was not created by ApplicationLoaders // In this case we create an isolated not-shared namespace for it. const std::string empty_dex_path; Result res = CreateClassLoaderNamespaceLocked(env, target_sdk_version, class_loader, nativeloader::API_DOMAIN_DEFAULT, /*is_shared=*/false, empty_dex_path, library_path_j, /*permitted_path_j=*/nullptr, /*uses_library_list_j=*/nullptr); if (!res.ok()) { ALOGD("Failed to create isolated ns for %s (caller=%s)", path, caller_location == nullptr ? "" : caller_location); *error_msg = strdup(res.error().message().c_str()); return nullptr; } ns = res.value(); ns_descr = "isolated"; } } *needs_native_bridge = ns->IsBridged(); Result handle = ns->Load(path); ALOGD("Load %s using %s ns %s (caller=%s): %s", path, ns_descr, ns->name().c_str(), caller_location == nullptr ? "" : caller_location, handle.ok() ? "ok" : handle.error().message().c_str()); if (!handle.ok()) { *error_msg = strdup(handle.error().message().c_str()); return nullptr; } return handle.value(); #else // !ART_TARGET_ANDROID UNUSED(env, target_sdk_version, class_loader, caller_location); // Do some best effort to emulate library-path support. It will not // work for dependencies. // // Note: null has a special meaning and must be preserved. std::string library_path; // Empty string by default. if (library_path_j != nullptr && path != nullptr && path[0] != '/') { ScopedUtfChars library_path_utf_chars(env, library_path_j); library_path = library_path_utf_chars.c_str(); } std::vector library_paths = base::Split(library_path, ":"); for (const std::string& lib_path : library_paths) { *needs_native_bridge = false; const char* path_arg; std::string complete_path; if (path == nullptr) { // Preserve null. path_arg = nullptr; } else { complete_path = lib_path; if (!complete_path.empty()) { complete_path.append("/"); } complete_path.append(path); path_arg = complete_path.c_str(); } void* handle = dlopen(path_arg, RTLD_NOW); if (handle != nullptr) { return handle; } if (NativeBridgeIsSupported(path_arg)) { *needs_native_bridge = true; handle = NativeBridgeLoadLibrary(path_arg, RTLD_NOW); if (handle != nullptr) { return handle; } *error_msg = strdup(NativeBridgeGetError()); } else { *error_msg = strdup(dlerror()); } } return nullptr; #endif // !ART_TARGET_ANDROID } bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) { bool success; if (needs_native_bridge) { success = (NativeBridgeUnloadLibrary(handle) == 0); if (!success) { *error_msg = strdup(NativeBridgeGetError()); } } else { success = (dlclose(handle) == 0); if (!success) { *error_msg = strdup(dlerror()); } } return success; } void NativeLoaderFreeErrorMessage(char* msg) { // The error messages get allocated through strdup, so we must call free on them. free(msg); } #if defined(ART_TARGET_ANDROID) void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge, char** error_msg) { Result handle = ns->Load(path); if (!handle.ok() && error_msg != nullptr) { *error_msg = strdup(handle.error().message().c_str()); } if (needs_native_bridge != nullptr) { *needs_native_bridge = ns->IsBridged(); } return handle.ok() ? *handle : nullptr; } bool IsNamespaceNativeBridged(const struct NativeLoaderNamespace* ns) { return ns->IsBridged(); } // native_bridge_namespaces are not supported for callers of this function. // This function will return nullptr in the case when application is running // on native bridge. android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) { std::lock_guard guard(g_namespaces_mutex); NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader); if (ns != nullptr && !ns->IsBridged()) { return ns->ToRawAndroidNamespace(); } return nullptr; } NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) { std::lock_guard guard(g_namespaces_mutex); return g_namespaces->FindNamespaceByClassLoader(env, class_loader); } void LinkNativeLoaderNamespaceToExportedNamespaceLibrary(struct NativeLoaderNamespace* ns, const char* exported_ns_name, const char* library_name, char** error_msg) { Result exported_ns = NativeLoaderNamespace::GetExportedNamespace(exported_ns_name, ns->IsBridged()); if (!exported_ns.ok()) { *error_msg = strdup(exported_ns.error().message().c_str()); return; } Result linked = ns->Link(&exported_ns.value(), std::string(library_name)); if (!linked.ok()) { *error_msg = strdup(linked.error().message().c_str()); } } #endif // ART_TARGET_ANDROID } // namespace android