/* * Copyright (C) 2023 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. */ #include "native_bridge.h" #include #include #include #include #include #include #include #include #include #include #include "procinfo/process_map.h" #include "berberis/base/algorithm.h" #include "berberis/base/bit_util.h" #include "berberis/base/config_globals.h" #include "berberis/base/file.h" #include "berberis/base/logging.h" #include "berberis/base/strings.h" #include "berberis/base/tracing.h" #include "berberis/guest_abi/guest_call.h" #include "berberis/guest_loader/guest_loader.h" #include "berberis/guest_os_primitives/guest_map_shadow.h" #include "berberis/guest_state/guest_addr.h" #include "berberis/jni/jni_trampolines.h" #include "berberis/native_activity/native_activity_wrapper.h" #include "berberis/native_bridge/native_bridge.h" #include "berberis/runtime/berberis.h" #include "berberis/runtime_primitives/known_guest_function_wrapper.h" #define LOG_NB ALOGV // redefine to ALOGD for debugging extern "C" { // Extended android loader functions for namespace management bool android_init_anonymous_namespace(const char* shared_libs_sonames, const char* library_search_path); struct android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type, const char* permitted_when_isolated_path, struct android_namespace_t* parent); bool android_link_namespaces(struct android_namespace_t* from, struct android_namespace_t* to, const char* shared_libs_sonames); struct android_namespace_t* android_get_exported_namespace(const char* name); } // extern "C" namespace android { // We maintain host namespace here to provide ability to open host // libraries via native bridge. See http://b/308371292 for details. struct native_bridge_namespace_t { android_namespace_t* guest_namespace; android_namespace_t* host_namespace; }; } // namespace android namespace { // See android/system/core/libnativebridge/native_bridge.cc // Even thought berberis does not support early version of NB interface // (deprecated methods do not work anymore) v2 support is needed to have NB call // getSignalHandler function. const constexpr uint32_t kNativeBridgeCallbackMinVersion = 2; const constexpr uint32_t kNativeBridgeCallbackVersion = 7; const constexpr uint32_t kNativeBridgeCallbackMaxVersion = kNativeBridgeCallbackVersion; const android::NativeBridgeRuntimeCallbacks* g_runtime_callbacks = nullptr; using native_bridge_namespace_t = android::native_bridge_namespace_t; using GuestAddr = berberis::GuestAddr; // Treble uses "sphal" name for the vendor namespace. constexpr const char* kVendorNamespaceName = "sphal"; class NdktNativeBridge { public: NdktNativeBridge(); ~NdktNativeBridge(); bool Initialize(std::string* error_msg); void* LoadLibrary(const char* libpath, int flags, const native_bridge_namespace_t* ns = nullptr); void* LoadGuestLibrary(const char* libpath, int flags, const native_bridge_namespace_t* ns = nullptr); GuestAddr DlSym(void* handle, const char* name); const char* DlError(); bool InitAnonymousNamespace(const char* public_ns_sonames, const char* anon_ns_library_path); native_bridge_namespace_t* CreateNamespace(const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type, const char* permitted_when_isolated_path, native_bridge_namespace_t* parent_ns); native_bridge_namespace_t* GetExportedNamespace(const char* name); bool LinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to, const char* shared_libs_sonames); bool IsHostHandle(void* handle) const; private: bool FinalizeInit(); native_bridge_namespace_t* CreateNativeBridgeNamespace(android_namespace_t* host_namespace, android_namespace_t* guest_namespace); void AddHostLibrary(void* handle); berberis::GuestLoader* guest_loader_; mutable std::mutex host_libraries_lock_; std::set host_libraries_; std::mutex namespaces_lock_; std::deque namespaces_; std::map exported_namespaces_; }; NdktNativeBridge::NdktNativeBridge() : guest_loader_(nullptr) {} NdktNativeBridge::~NdktNativeBridge() {} bool NdktNativeBridge::Initialize(std::string* error_msg) { guest_loader_ = berberis::GuestLoader::StartAppProcessInNewThread(error_msg); berberis::RegisterKnownGuestFunctionWrapper("JNI_OnLoad", berberis::WrapGuestJNIOnLoad); berberis::RegisterKnownGuestFunctionWrapper("ANativeActivity_onCreate", berberis::WrapGuestNativeActivityOnCreate); return guest_loader_ != nullptr; } void* NdktNativeBridge::LoadLibrary(const char* libpath, int flags, const native_bridge_namespace_t* ns) { // We don't have a callback after all java initialization is finished. So we call the finalizing // routine from here, just before we load any app's native code. static bool init_finalized = FinalizeInit(); UNUSED(init_finalized); void* handle = LoadGuestLibrary(libpath, flags, ns); if (handle != nullptr) { return handle; } // http://b/206676167: Do not fallback to host for libRS.so if (berberis::Basename(libpath) == "libRS.so") { return handle; } // Try falling back to host loader. android_dlextinfo extinfo_holder; android_dlextinfo* extinfo = nullptr; if (ns != nullptr) { extinfo_holder.flags = ANDROID_DLEXT_USE_NAMESPACE; extinfo_holder.library_namespace = ns->host_namespace; extinfo = &extinfo_holder; } handle = android_dlopen_ext(libpath, flags, extinfo); if (handle != nullptr) { ALOGI("'%s' library was loaded for the host platform.", libpath); AddHostLibrary(handle); } return handle; } void* NdktNativeBridge::LoadGuestLibrary(const char* libpath, int flags, const native_bridge_namespace_t* ns) { android_dlextinfo extinfo_holder; android_dlextinfo* extinfo = nullptr; if (ns != nullptr) { extinfo_holder.flags = ANDROID_DLEXT_USE_NAMESPACE; extinfo_holder.library_namespace = ns->guest_namespace; extinfo = &extinfo_holder; } return guest_loader_->DlOpenExt(libpath, flags, extinfo); } void NdktNativeBridge::AddHostLibrary(void* handle) { const std::lock_guard guard(host_libraries_lock_); host_libraries_.insert(handle); } bool NdktNativeBridge::IsHostHandle(void* handle) const { const std::lock_guard guard(host_libraries_lock_); return berberis::Contains(host_libraries_, handle); } GuestAddr NdktNativeBridge::DlSym(void* handle, const char* name) { CHECK(!IsHostHandle(handle)); return guest_loader_->DlSym(handle, name); } const char* NdktNativeBridge::DlError() { // There is no good way of knowing where the error happened, - prioritize the guest loader. const char* error = guest_loader_->DlError(); if (error != nullptr) { return error; } return dlerror(); } native_bridge_namespace_t* NdktNativeBridge::CreateNamespace( const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type, const char* permitted_when_isolated_path, native_bridge_namespace_t* parent_ns) { // Android SDK libraries do not have a good mechanism for using JNI libraries. // The only way to make it work is to put them to system search path and make // the library public (visible from apps). See http://b/308371292 for details. // // Since `ClassLoader.findLibrary` is looking for the library in 'java.library.path' // in addition to paths used here it is able to find a JNI library located in system // library path. If then such a library appears to be a public library, the android // loader will be able to load it from the system linker namespace. // // It could also happen so that the app puts different architecture libraries // in the same folder (say x86_64 libraries to arm64 folder), in which case // they will work if the architecture happens to match with host one. This is // why we preserve guest search path for the host namespace. auto* host_namespace = android_create_namespace(name, ld_library_path, default_library_path, type, permitted_when_isolated_path, parent_ns->host_namespace); auto* guest_namespace = guest_loader_->CreateNamespace(name, ld_library_path, default_library_path, type, permitted_when_isolated_path, parent_ns->guest_namespace); return CreateNativeBridgeNamespace(host_namespace, guest_namespace); } native_bridge_namespace_t* NdktNativeBridge::GetExportedNamespace(const char* name) { const std::lock_guard guard(namespaces_lock_); auto it = exported_namespaces_.find(name); if (it != exported_namespaces_.end()) { return &it->second; } auto host_namespace = android_get_exported_namespace(name); auto guest_namespace = guest_loader_->GetExportedNamespace(name); auto [insert_it, inserted] = exported_namespaces_.try_emplace(std::string(name), native_bridge_namespace_t{.guest_namespace = guest_namespace, .host_namespace = host_namespace}); CHECK(inserted); return &insert_it->second; } bool NdktNativeBridge::InitAnonymousNamespace(const char* public_ns_sonames, const char* anon_ns_library_path) { return guest_loader_->InitAnonymousNamespace(public_ns_sonames, anon_ns_library_path) && android_init_anonymous_namespace(public_ns_sonames, anon_ns_library_path); } bool NdktNativeBridge::LinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to, const char* shared_libs_sonames) { return guest_loader_->LinkNamespaces( from->guest_namespace, to->guest_namespace, shared_libs_sonames) && android_link_namespaces(from->host_namespace, to->host_namespace, shared_libs_sonames); } native_bridge_namespace_t* NdktNativeBridge::CreateNativeBridgeNamespace( android_namespace_t* host_namespace, android_namespace_t* guest_namespace) { const std::lock_guard guard(namespaces_lock_); namespaces_.emplace_back(native_bridge_namespace_t{.guest_namespace = guest_namespace, .host_namespace = host_namespace}); return &namespaces_.back(); } void ProtectMappingsFromGuest() { auto callback = [](uint64_t start, uint64_t end, uint16_t, uint64_t, ino_t, const char* libname_c_str, bool) { std::string_view libname(libname_c_str); // Per analysis in b/218772975 only libc is affected. It's occasionally either proxy libc or // guest libc. So we protect all libs with "libc.so" substring. At this point no app's libs // are loaded yet, so the app shouldn't tamper with the already loaded ones. We don't // protect all the already loaded libraries though since GuestMapShadow isn't optimized // to work with large number of entries. Also some of them could be unmapped later, which is // not expected for libc.so. if (libname.find("libc.so") != std::string_view::npos) { berberis::GuestMapShadow::GetInstance()->AddProtectedMapping( berberis::bit_cast(static_cast(start)), berberis::bit_cast(static_cast(end))); } }; android::procinfo::ReadMapFile("/proc/self/maps", callback); } extern "C" const char* __progname; bool NdktNativeBridge::FinalizeInit() { // Guest-libc is expected to be loaded along with app-process during Initialize(). At that time // __progname isn't yet initialized in java. So now when it should be initialized we copy it over // from host to guest. // Note that we cannot delay Initialize() (and hence guest-libc loading) until now because // guest_loader initialized there is then used to create and link linker namespaces. // We cannot cannot unload (dlclose) guest-libc after app-process loading either (intending to // reload it now to get the updated __progname), since guest_linker is already tightly linked with // it. // Force libc loading if it's not loaded yet to ensure the symbol is overridden. // We do not call LoadLibrary since it'd recurse back into FinalizeInit. void* libc = guest_loader_->DlOpenExt("libc.so", RTLD_NOW, nullptr); CHECK_NE(libc, nullptr); auto addr = DlSym(libc, "__progname"); CHECK_NE(addr, berberis::kNullGuestAddr); memcpy(berberis::ToHostAddr(addr), &__progname, sizeof(__progname)); // Now, when guest libc and proxy-libc are loaded, // remember mappings which guest code must not tamper with. ProtectMappingsFromGuest(); return true; } NdktNativeBridge g_ndkt_native_bridge; // Runtime values must be non-NULL, otherwise native bridge will be disabled. // Note, that 'supported_abis' and 'abi_count' are deprecated (b/18061712). const struct android::NativeBridgeRuntimeValues* GetAppEnvByIsa(const char* app_isa) { if (app_isa == nullptr) { ALOGE("instruction set is null"); return nullptr; } if (strcmp(app_isa, berberis::kGuestIsa) == 0) { return &berberis::kNativeBridgeRuntimeValues; } ALOGE("unknown instruction set '%s'", app_isa); return nullptr; } void SetAppPropertiesFromCodeCachePath(const char* private_dir) { if (private_dir == nullptr) { return; } // Expect private_dir to be ...//code_cache std::string_view path(private_dir); if (!berberis::ConsumeSuffix(&path, "/code_cache")) { return; } berberis::SetAppPrivateDir(path); auto begin = path.find_last_of('/'); if (begin == std::string_view::npos) { return; } berberis::SetAppPackageName(path.substr(begin + 1)); } bool native_bridge_initialize(const android::NativeBridgeRuntimeCallbacks* runtime_cbs, const char* private_dir, const char* instruction_set) { LOG_NB("native_bridge_initialize(runtime_callbacks=%p, private_dir='%s', app_isa='%s')", runtime_cbs, private_dir ? private_dir : "(null)", instruction_set ? instruction_set : "(null)"); auto* env = GetAppEnvByIsa(instruction_set); if (env == nullptr) { return false; } g_runtime_callbacks = runtime_cbs; SetAppPropertiesFromCodeCachePath(private_dir); berberis::InitBerberis(); char version[PROP_VALUE_MAX]; if (__system_property_get("ro.berberis.version", version)) { ALOGI("Initialized Berberis (%s), version %s", env->os_arch, version); } else { ALOGI("Initialized Berberis (%s)", env->os_arch); } std::string error_msg; if (!g_ndkt_native_bridge.Initialize(&error_msg)) { LOG_ALWAYS_FATAL("native_bridge_initialize: %s", error_msg.c_str()); } return true; } void* native_bridge_loadLibrary(const char* libpath, int flag) { // We should only get here if this library is not native. LOG_NB("native_bridge_loadLibrary(path='%s', flag=0x%x)", libpath ? libpath : "(null)", flag); return g_ndkt_native_bridge.LoadLibrary(libpath, flag); } void* native_bridge_getTrampolineWithJNICallType(void* handle, const char* name, const char* shorty, uint32_t len, enum android::JNICallType jni_call_type) { LOG_NB( "native_bridge_getTrampolineWithJNICallType(handle=%p, name='%s', shorty='%s', len=%d, " "jni_call_type=%d)", handle, name ? name : "(null)", shorty ? shorty : "(null)", len, jni_call_type); if (g_ndkt_native_bridge.IsHostHandle(handle)) { return dlsym(handle, name); } GuestAddr guest_addr = g_ndkt_native_bridge.DlSym(handle, name); if (!guest_addr) { return nullptr; } if (shorty) { return const_cast(berberis::WrapGuestJNIFunction( guest_addr, shorty, name, jni_call_type != android::JNICallType::kJNICallTypeCriticalNative)); } berberis::HostCode result = berberis::WrapKnownGuestFunction(guest_addr, name); if (result == nullptr) { // No wrapper is registered for this function name. // This usually happens for ANativeActivity_onCreate renamed with android.app.func_name. // TODO(b/27307664): maybe query android.app.func_name from Java and check exactly? TRACE("No wrapper is registered for %s, assume it's ANativeActivity_onCreate", name); result = berberis::WrapKnownGuestFunction(guest_addr, "ANativeActivity_onCreate"); } return const_cast(result); } void* native_bridge_getTrampolineForFunctionPointer(const void* method, const char* shorty, uint32_t len, enum android::JNICallType jni_call_type) { LOG_NB( "native_bridge_getTrampolineForFunctionPointer(method=%p, shorty='%s', len=%d, " "jni_call_type=%d)", method, shorty ? shorty : "(null)", len, jni_call_type); auto guest_addr = berberis::ToGuestAddr(method); if (!berberis::GuestMapShadow::GetInstance()->IsExecutable(guest_addr, 1)) { // This is not guest code - happens when native_bridge falls back // to host libraries. return const_cast(method); } return const_cast(berberis::WrapGuestJNIFunction( guest_addr, shorty, "(unknown)", jni_call_type != android::JNICallType::kJNICallTypeCriticalNative)); } void* native_bridge_getTrampoline(void* handle, const char* name, const char* shorty, uint32_t len) { LOG_NB( "Warning: Unexpected call to native_bridge_getTrampoline (old android version?), converting " "to a native_bridge_getTrampolineWithJNICallType call with kJNICallTyepRegular"); return native_bridge_getTrampolineWithJNICallType( handle, name, shorty, len, android::JNICallType::kJNICallTypeRegular); } bool native_bridge_isSupported(const char* libpath) { LOG_NB("native_bridge_isSupported(path='%s')", libpath ? libpath : "(null)"); return true; } const struct android::NativeBridgeRuntimeValues* native_bridge_getAppEnv( const char* instruction_set) { LOG_NB("native_bridge_getAppEnv(app_isa='%s')", instruction_set ? instruction_set : "(null)"); return GetAppEnvByIsa(instruction_set); } bool native_bridge_isCompatibleWith(uint32_t bridge_version) { LOG_NB("native_bridge_isCompatibleWith(bridge_version=%d)", bridge_version); return bridge_version >= kNativeBridgeCallbackMinVersion && bridge_version <= kNativeBridgeCallbackMaxVersion; } android::NativeBridgeSignalHandlerFn native_bridge_getSignalHandler(int signal) { LOG_NB("native_bridge_getSignalHandler(signal=%d)", signal); return nullptr; } int native_bridge_unloadLibrary(void* handle) { LOG_NB("native_bridge_unloadLibrary(handle=%p)", handle); // TODO(b/276787500): support library unloading! return 0; } const char* native_bridge_getError() { LOG_NB("native_bridge_getError()"); return g_ndkt_native_bridge.DlError(); } bool native_bridge_isPathSupported(const char* library_path) { LOG_NB("native_bridge_isPathSupported(path=%s)", library_path); return strstr(library_path, berberis::kSupportedLibraryPathSubstring) != nullptr; } bool native_bridge_initAnonymousNamespace(const char* public_ns_sonames, const char* anon_ns_library_path) { LOG_NB("native_bridge_initAnonymousNamespace(public_ns_sonames=%s, anon_ns_library_path=%s)", public_ns_sonames, anon_ns_library_path); return g_ndkt_native_bridge.InitAnonymousNamespace(public_ns_sonames, anon_ns_library_path); } native_bridge_namespace_t* native_bridge_createNamespace(const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type, const char* permitted_when_isolated_path, native_bridge_namespace_t* parent_ns) { LOG_NB("native_bridge_createNamespace(name=%s, path=%s)", name, ld_library_path); return g_ndkt_native_bridge.CreateNamespace( name, ld_library_path, default_library_path, type, permitted_when_isolated_path, parent_ns); } bool native_bridge_linkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to, const char* shared_libs_sonames) { LOG_NB("native_bridge_linkNamespaces(from=%p, to=%p, shared_libs=%s)", from, to, shared_libs_sonames); return g_ndkt_native_bridge.LinkNamespaces(from, to, shared_libs_sonames); } void* native_bridge_loadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) { LOG_NB("native_bridge_loadLibraryExt(path=%s)", libpath); return g_ndkt_native_bridge.LoadLibrary(libpath, flag, ns); } native_bridge_namespace_t* native_bridge_getVendorNamespace() { LOG_NB("native_bridge_getVendorNamespace()"); // This method is retained for backwards compatibility. return g_ndkt_native_bridge.GetExportedNamespace(kVendorNamespaceName); } native_bridge_namespace_t* native_bridge_getExportedNamespace(const char* name) { LOG_NB("native_bridge_getExportedNamespace(name=%s)", name); return g_ndkt_native_bridge.GetExportedNamespace(name); } void native_bridge_preZygoteFork() { // In case of app-zygote the translator could have executed some guest code // during app-zygote's doPreload(). Zygote's fork doesn't allow unrecognized // open file descriptors, so we close them. // // We assume that all guest execution has finished in doPreload() and there // are no background guest threads. ART ensures the fork is single-threaded by // calling waitUntilAllThreadsStopped() in ZygoteHooks::preZork(). // TODO(b/188923523): Technically this happens after nativePreFork() (which // calls this callback), so theoretically some guest thread may still be // running and finishes later. If this happens to be an issue, we can call an // analog of waitUntilAllThreadsStopped() here. Or try to call nativePreFork() // after waitUntilAllThreadsStopped() in ART. // TODO(b/188923523): Consider moving to berberis::GuestPreZygoteFork(). void* liblog = g_ndkt_native_bridge.LoadGuestLibrary("liblog.so", RTLD_NOLOAD); // Nothing to close if the guest library hasn't been loaded. if (liblog) { auto addr = g_ndkt_native_bridge.DlSym(liblog, "__android_log_close"); CHECK_NE(addr, berberis::kNullGuestAddr); berberis::GuestCall call; call.RunVoid(addr); } berberis::PreZygoteForkUnsafe(); } } // namespace namespace berberis { const char* GetJMethodShorty(JNIEnv* env, jmethodID mid) { CHECK(g_runtime_callbacks); return (g_runtime_callbacks->getMethodShorty)(env, mid); } } // namespace berberis extern "C" { // "NativeBridgeItf" is effectively an API (it is the name of the symbol that // will be loaded by the native bridge library). android::NativeBridgeCallbacks NativeBridgeItf = { kNativeBridgeCallbackVersion, &native_bridge_initialize, &native_bridge_loadLibrary, &native_bridge_getTrampoline, &native_bridge_isSupported, &native_bridge_getAppEnv, &native_bridge_isCompatibleWith, &native_bridge_getSignalHandler, &native_bridge_unloadLibrary, &native_bridge_getError, &native_bridge_isPathSupported, &native_bridge_initAnonymousNamespace, &native_bridge_createNamespace, &native_bridge_linkNamespaces, &native_bridge_loadLibraryExt, &native_bridge_getVendorNamespace, &native_bridge_getExportedNamespace, &native_bridge_preZygoteFork, &native_bridge_getTrampolineWithJNICallType, &native_bridge_getTrampolineForFunctionPointer, }; } // extern "C"