From 0d0f8df5070dc6c6e5a5809d8a57e0212a1a5164 Mon Sep 17 00:00:00 2001 From: Martin Stjernholm Date: Wed, 28 Apr 2021 16:47:01 +0100 Subject: In run tests, do not rely on loading native libs in the ART namespace with an allow_all_shared_libs fallback to system. The system and com_android_art namespaces need to be properly separated, but run tests have relied on loading test libraries through either LD_LIBRARY_PATH or java.library.path without a clear separation. That has worked through a combination of ANDROID_ADDITIONAL_PUBLIC_LIBRARIES and fallback dlopen() calls that used the ART namespace. This change introduces a new directory /data/nativetest(64)/com.android.art for test libraries that depend on internal ART libraries. It's added with LD_LIBRARY_PATH to the default namespace, which in the APEX linker config has full access to com_android_art. Normal JNI libraries that don't depend on ART internals stay in /data/nativetest(64)/art/. There should be no overlap between the two locations. A new environment variable NATIVELOADER_DEFAULT_NAMESPACE_LIBS is introduced to list the libraries added through LD_LIBRARY_PATH, so libnativeloader can link to them from classloader namespaces and in the fallback namespace when no classloader is specified. Like ANDROID_ADDITIONAL_PUBLIC_LIBRARIES, NATIVELOADER_DEFAULT_NAMESPACE_LIBS is only effective when ro.debuggable is true. A new cc_defaults "art_test_internal_library_defaults" is added to Android.bp, to be used in libraries that should be installed in the new com.android.art directory. Some run tests that are special and need more treatment are disabled for now, to be addressed in b/186654484. Test: art/test/testrunner/testrunner.py --target --64 --optimizing Test: art/test/testrunner/testrunner.py --target --32 --jit Test: art/test/testrunner/testrunner.py --host --64 --optimizing Bug: 130340935 Change-Id: Iec640d5e22b46af2c1a4d375ce3f06c57b1d224e --- libnativeloader/native_loader.cpp | 175 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 170 insertions(+), 5 deletions(-) (limited to 'libnativeloader/native_loader.cpp') diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp index 64d60ea6cc..b34692ae16 100644 --- a/libnativeloader/native_loader.cpp +++ b/libnativeloader/native_loader.cpp @@ -29,7 +29,9 @@ #include #include +#include #include +#include #include #include @@ -42,11 +44,36 @@ namespace android { namespace { + #if defined(ART_TARGET_ANDROID) + +// NATIVELOADER_DEFAULT_NAMESPACE_LIBS is an environment variable that can be +// used when ro.debuggable is true 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"; + +bool Debuggable() { + static bool debuggable = android::base::GetBoolProperty("ro.debuggable", false); + return debuggable; +} + using android::nativeloader::LibraryNamespaces; std::mutex g_namespaces_mutex; LibraryNamespaces* g_namespaces = new LibraryNamespaces; +NativeLoaderNamespace* g_nativeloader_extra_libs_namespace = nullptr; android_namespace_t* FindExportedNamespace(const char* caller_location) { auto name = nativeloader::FindApexNamespaceName(caller_location); @@ -58,7 +85,98 @@ android_namespace_t* FindExportedNamespace(const char* caller_location) { } return nullptr; } + +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. This is only enabled if the ro.debuggable is true. +Result TryLoadNativeloaderExtraLib(const char* path) { + if (!Debuggable()) { + return nullptr; + } + 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(); + } + return ns.value()->Load(path); +} + +Result CreateClassLoaderNamespaceLocked(JNIEnv* env, + int32_t target_sdk_version, + jobject class_loader, + bool is_shared, + jstring dex_path, + jstring library_path, + jstring permitted_path, + jstring uses_library_list) + REQUIRES(g_namespaces_mutex) { + Result ns = g_namespaces->Create(env, + target_sdk_version, + class_loader, + is_shared, + dex_path, + library_path, + permitted_path, + uses_library_list); + if (!ns.ok()) { + return ns; + } + if (Debuggable()) { + Result linked = CreateNativeloaderDefaultNamespaceLibsLink(*ns.value()); + if (!linked.ok()) { + return linked.error(); + } + } + return ns; +} + #endif // #if defined(ART_TARGET_ANDROID) + } // namespace void InitializeNativeLoader() { @@ -72,6 +190,8 @@ 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 } @@ -80,8 +200,14 @@ jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobj jstring permitted_path, jstring uses_library_list) { #if defined(ART_TARGET_ANDROID) std::lock_guard guard(g_namespaces_mutex); - auto ns = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path, - library_path, permitted_path, uses_library_list); + Result ns = CreateClassLoaderNamespaceLocked(env, + target_sdk_version, + class_loader, + is_shared, + dex_path, + library_path, + permitted_path, + uses_library_list); if (!ns.ok()) { return env->NewStringUTF(ns.error().message().c_str()); } @@ -97,6 +223,7 @@ void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* pat bool* needs_native_bridge, char** error_msg) { #if defined(ART_TARGET_ANDROID) UNUSED(target_sdk_version); + if (class_loader == nullptr) { *needs_native_bridge = false; if (caller_location != nullptr) { @@ -113,6 +240,20 @@ void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* pat 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(); + } + } + void* handle = dlopen(path, RTLD_NOW); if (handle == nullptr) { *error_msg = strdup(dlerror()); @@ -127,8 +268,14 @@ void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* pat // This is the case where the classloader was not created by ApplicationLoaders // In this case we create an isolated not-shared namespace for it. Result isolated_ns = - g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */, nullptr, - library_path, nullptr, nullptr); + CreateClassLoaderNamespaceLocked(env, + target_sdk_version, + class_loader, + /*is_shared=*/false, + /*dex_path=*/nullptr, + library_path, + /*permitted_path=*/nullptr, + /*uses_library_list=*/nullptr); if (!isolated_ns.ok()) { *error_msg = strdup(isolated_ns.error().message().c_str()); return nullptr; @@ -238,6 +385,24 @@ NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobje std::lock_guard guard(g_namespaces_mutex); return g_namespaces->FindNamespaceByClassLoader(env, class_loader); } -#endif + +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 -- cgit v1.2.3-59-g8ed1b