Hide non-bridged/bridged namespaces behind NativeLoaderNamespace class

NativeLoaderNamespace fully abstracts the non-bridged (so called
android-) and bridged namespaces.

Bug: 130388701
Test: build & pass presubmit tests
Change-Id: I3d5ca7515711e7638f4a5ab4d3a150185c3d17ac
This commit is contained in:
Jiyong Park 2019-05-04 00:30:23 +09:00
parent 3afe5f22b3
commit 8537781cc7
5 changed files with 212 additions and 187 deletions

View File

@ -30,6 +30,7 @@ cc_library {
android: {
srcs: [
"library_namespaces.cpp",
"native_loader_namespace.cpp",
"public_libraries.cpp",
],
shared_libs: [

View File

@ -41,8 +41,6 @@ namespace {
// vendor and system namespaces.
constexpr const char* kVendorNamespaceName = "sphal";
constexpr const char* kVndkNamespaceName = "vndk";
constexpr const char* kDefaultNamespaceName = "default";
constexpr const char* kPlatformNamespaceName = "platform";
constexpr const char* kRuntimeNamespaceName = "runtime";
// classloader-namespace is a linker namespace that is created for the loaded
@ -167,34 +165,13 @@ NativeLoaderNamespace* LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sd
LOG_ALWAYS_FATAL_IF(found, "There is already a namespace associated with this classloader");
uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
if (is_shared) {
namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
}
if (target_sdk_version < 24) {
namespace_type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
}
NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
bool is_native_bridge = false;
if (parent_ns != nullptr) {
is_native_bridge = !parent_ns->is_android_namespace();
} else if (!library_path.empty()) {
is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str());
}
std::string system_exposed_libraries = default_public_libraries();
const char* namespace_name = kClassloaderNamespaceName;
android_namespace_t* vndk_ns = nullptr;
bool unbundled_vendor_or_product_app = false;
if ((apk_origin == APK_ORIGIN_VENDOR ||
(apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) &&
!is_shared) {
LOG_FATAL_IF(is_native_bridge,
"Unbundled vendor / product apk must not use translated architecture");
unbundled_vendor_or_product_app = true;
// For vendor / product apks, give access to the vendor / product lib even though
// they are treated as unbundled; the libs and apks are still bundled
// together in the vendor / product partition.
@ -214,22 +191,12 @@ NativeLoaderNamespace* LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sd
origin_partition = "unknown";
origin_lib_path = "";
}
LOG_FATAL_IF(is_native_bridge, "Unbundled %s apk must not use translated architecture",
origin_partition);
library_path = library_path + ":" + origin_lib_path;
permitted_path = permitted_path + ":" + origin_lib_path;
// Also give access to LLNDK libraries since they are available to vendors
system_exposed_libraries = system_exposed_libraries + ":" + llndk_libraries().c_str();
// Give access to VNDK-SP libraries from the 'vndk' namespace.
vndk_ns = android_get_exported_namespace(kVndkNamespaceName);
if (vndk_ns == nullptr) {
ALOGW("Cannot find \"%s\" namespace for %s apks", kVndkNamespaceName, origin_partition);
}
// Different name is useful for debugging
namespace_name = kVendorClassloaderNamespaceName;
ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
@ -241,120 +208,56 @@ NativeLoaderNamespace* LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sd
system_exposed_libraries = system_exposed_libraries + ':' + extended_public_libraries();
}
}
std::string runtime_exposed_libraries = runtime_public_libraries();
NativeLoaderNamespace native_loader_ns;
if (!is_native_bridge) {
// The platform namespace is called "default" for binaries in /system and
// "platform" for those in the Runtime APEX. Try "platform" first since
// "default" always exists.
android_namespace_t* platform_ns = android_get_exported_namespace(kPlatformNamespaceName);
if (platform_ns == nullptr) {
platform_ns = android_get_exported_namespace(kDefaultNamespaceName);
}
android_namespace_t* android_parent_ns;
if (parent_ns != nullptr) {
android_parent_ns = parent_ns->get_android_ns();
} else {
// Fall back to the platform namespace if no parent is found.
android_parent_ns = platform_ns;
}
android_namespace_t* ns =
android_create_namespace(namespace_name, nullptr, library_path.c_str(), namespace_type,
permitted_path.c_str(), android_parent_ns);
if (ns == nullptr) {
*error_msg = dlerror();
return nullptr;
}
// Note that when vendor_ns is not configured this function will return nullptr
// and it will result in linking vendor_public_libraries_ to the default namespace
// which is expected behavior in this case.
android_namespace_t* vendor_ns = android_get_exported_namespace(kVendorNamespaceName);
android_namespace_t* runtime_ns = android_get_exported_namespace(kRuntimeNamespaceName);
if (!android_link_namespaces(ns, platform_ns, system_exposed_libraries.c_str())) {
*error_msg = dlerror();
return nullptr;
}
// Runtime apex does not exist in host, and under certain build conditions.
if (runtime_ns != nullptr) {
if (!android_link_namespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
*error_msg = dlerror();
return nullptr;
}
}
if (vndk_ns != nullptr && !vndksp_libraries().empty()) {
// vendor apks are allowed to use VNDK-SP libraries.
if (!android_link_namespaces(ns, vndk_ns, vndksp_libraries().c_str())) {
*error_msg = dlerror();
return nullptr;
}
}
if (!vendor_public_libraries().empty()) {
if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries().c_str())) {
*error_msg = dlerror();
return nullptr;
}
}
native_loader_ns = NativeLoaderNamespace(ns);
} else {
// Same functionality as in the branch above, but calling through native bridge.
native_bridge_namespace_t* platform_ns =
NativeBridgeGetExportedNamespace(kPlatformNamespaceName);
if (platform_ns == nullptr) {
platform_ns = NativeBridgeGetExportedNamespace(kDefaultNamespaceName);
}
native_bridge_namespace_t* native_bridge_parent_namespace;
if (parent_ns != nullptr) {
native_bridge_parent_namespace = parent_ns->get_native_bridge_ns();
} else {
native_bridge_parent_namespace = platform_ns;
}
native_bridge_namespace_t* ns =
NativeBridgeCreateNamespace(namespace_name, nullptr, library_path.c_str(), namespace_type,
permitted_path.c_str(), native_bridge_parent_namespace);
if (ns == nullptr) {
*error_msg = NativeBridgeGetError();
return nullptr;
}
native_bridge_namespace_t* vendor_ns = NativeBridgeGetExportedNamespace(kVendorNamespaceName);
native_bridge_namespace_t* runtime_ns = NativeBridgeGetExportedNamespace(kRuntimeNamespaceName);
if (!NativeBridgeLinkNamespaces(ns, platform_ns, system_exposed_libraries.c_str())) {
*error_msg = NativeBridgeGetError();
return nullptr;
}
// Runtime apex does not exist in host, and under certain build conditions.
if (runtime_ns != nullptr) {
if (!NativeBridgeLinkNamespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
*error_msg = NativeBridgeGetError();
return nullptr;
}
}
if (!vendor_public_libraries().empty()) {
if (!NativeBridgeLinkNamespaces(ns, vendor_ns, vendor_public_libraries().c_str())) {
*error_msg = NativeBridgeGetError();
return nullptr;
}
}
native_loader_ns = NativeLoaderNamespace(ns);
// Create the app namespace
NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
auto app_ns =
NativeLoaderNamespace::Create(namespace_name, library_path, permitted_path, parent_ns,
is_shared, target_sdk_version < 24 /* is_greylist_enabled */);
if (app_ns.IsNil()) {
*error_msg = app_ns.GetError();
return nullptr;
}
namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
// ... and link to other namespaces to allow access to some public libraries
bool is_bridged = app_ns.IsBridged();
auto platform_ns = NativeLoaderNamespace::GetPlatformNamespace(is_bridged);
if (!app_ns.Link(platform_ns, system_exposed_libraries)) {
*error_msg = app_ns.GetError();
return nullptr;
}
auto runtime_ns = NativeLoaderNamespace::GetExportedNamespace(kRuntimeNamespaceName, is_bridged);
// Runtime apex does not exist in host, and under certain build conditions.
if (!runtime_ns.IsNil()) {
if (!app_ns.Link(runtime_ns, runtime_public_libraries())) {
*error_msg = app_ns.GetError();
return nullptr;
}
}
// Give access to VNDK-SP libraries from the 'vndk' namespace.
if (unbundled_vendor_or_product_app && !vndksp_libraries().empty()) {
auto vndk_ns = NativeLoaderNamespace::GetExportedNamespace(kVndkNamespaceName, is_bridged);
if (!vndk_ns.IsNil() && !app_ns.Link(vndk_ns, vndksp_libraries())) {
*error_msg = app_ns.GetError();
return nullptr;
}
}
// Note that when vendor_ns is not configured, vendor_ns.IsNil() will be true
// and it will result in linking to the default namespace which is expected
// behavior in this case.
if (!vendor_public_libraries().empty()) {
auto vendor_ns = NativeLoaderNamespace::GetExportedNamespace(kVendorNamespaceName, is_bridged);
if (!app_ns.Link(vendor_ns, vendor_public_libraries())) {
*error_msg = dlerror();
return nullptr;
}
}
namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), app_ns));
return &(namespaces_.back().second);
}

View File

@ -220,25 +220,12 @@ void NativeLoaderFreeErrorMessage(char* msg) {
#if defined(__ANDROID__)
void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
bool* needs_native_bridge, char** error_msg) {
if (ns->is_android_namespace()) {
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
extinfo.library_namespace = ns->get_android_ns();
void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
if (handle == nullptr) {
*error_msg = strdup(dlerror());
}
*needs_native_bridge = false;
return handle;
} else {
void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns->get_native_bridge_ns());
if (handle == nullptr) {
*error_msg = strdup(NativeBridgeGetError());
}
*needs_native_bridge = true;
return handle;
void* handle = ns->Load(path);
if (handle == nullptr) {
*error_msg = ns->GetError();
}
*needs_native_bridge = ns->IsBridged();
return handle;
}
// native_bridge_namespaces are not supported for callers of this function.
@ -247,10 +234,9 @@ void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
if (ns != nullptr) {
return ns->is_android_namespace() ? ns->get_android_ns() : nullptr;
if (ns != nullptr && !ns->IsBridged()) {
return ns->ToRawAndroidNamespace();
}
return nullptr;
}

View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2019 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 "native_loader_namespace.h"
#include <dlfcn.h>
#include <functional>
#include "android-base/strings.h"
#include "log/log.h"
#include "nativebridge/native_bridge.h"
#include "nativeloader/dlext_namespaces.h"
namespace android {
namespace {
constexpr const char* kDefaultNamespaceName = "default";
constexpr const char* kPlatformNamespaceName = "platform";
} // namespace
NativeLoaderNamespace NativeLoaderNamespace::GetExportedNamespace(const std::string& name,
bool is_bridged) {
if (!is_bridged) {
return NativeLoaderNamespace(name, android_get_exported_namespace(name.c_str()));
} else {
return NativeLoaderNamespace(name, NativeBridgeGetExportedNamespace(name.c_str()));
}
}
char* NativeLoaderNamespace::GetError() const {
if (!IsBridged()) {
return strdup(dlerror());
} else {
return strdup(NativeBridgeGetError());
}
}
// The platform namespace is called "default" for binaries in /system and
// "platform" for those in the Runtime APEX. Try "platform" first since
// "default" always exists.
NativeLoaderNamespace NativeLoaderNamespace::GetPlatformNamespace(bool is_bridged) {
NativeLoaderNamespace ns = GetExportedNamespace(kPlatformNamespaceName, is_bridged);
if (ns.IsNil()) {
ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged);
}
return ns;
}
NativeLoaderNamespace NativeLoaderNamespace::Create(const std::string& name,
const std::string& search_paths,
const std::string& permitted_paths,
const NativeLoaderNamespace* parent,
bool is_shared, bool is_greylist_enabled) {
bool is_bridged = false;
if (parent != nullptr) {
is_bridged = parent->IsBridged();
} else if (!search_paths.empty()) {
is_bridged = NativeBridgeIsPathSupported(search_paths.c_str());
}
// Fall back to the platform namespace if no parent is set.
const NativeLoaderNamespace& effective_parent =
parent != nullptr ? *parent : GetPlatformNamespace(is_bridged);
uint64_t type = ANDROID_NAMESPACE_TYPE_ISOLATED;
if (is_shared) {
type |= ANDROID_NAMESPACE_TYPE_SHARED;
}
if (is_greylist_enabled) {
type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED;
}
if (!is_bridged) {
android_namespace_t* raw =
android_create_namespace(name.c_str(), nullptr, search_paths.c_str(), type,
permitted_paths.c_str(), effective_parent.ToRawAndroidNamespace());
return NativeLoaderNamespace(name, raw);
} else {
native_bridge_namespace_t* raw = NativeBridgeCreateNamespace(
name.c_str(), nullptr, search_paths.c_str(), type, permitted_paths.c_str(),
effective_parent.ToRawNativeBridgeNamespace());
return NativeLoaderNamespace(name, raw);
}
}
bool NativeLoaderNamespace::Link(const NativeLoaderNamespace& target,
const std::string& shared_libs) const {
LOG_ALWAYS_FATAL_IF(shared_libs.empty(), "empty share lib when linking %s to %s",
this->name().c_str(), target.name().c_str());
if (!IsBridged()) {
return android_link_namespaces(this->ToRawAndroidNamespace(), target.ToRawAndroidNamespace(),
shared_libs.c_str());
} else {
return NativeBridgeLinkNamespaces(this->ToRawNativeBridgeNamespace(),
target.ToRawNativeBridgeNamespace(), shared_libs.c_str());
}
}
void* NativeLoaderNamespace::Load(const std::string& lib_name) const {
if (!IsBridged()) {
android_dlextinfo extinfo;
extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
extinfo.library_namespace = this->ToRawAndroidNamespace();
return android_dlopen_ext(lib_name.c_str(), RTLD_NOW, &extinfo);
} else {
return NativeBridgeLoadLibraryExt(lib_name.c_str(), RTLD_NOW,
this->ToRawNativeBridgeNamespace());
}
}
} // namespace android

View File

@ -16,13 +16,14 @@
#pragma once
#if defined(__ANDROID__)
#include <dlfcn.h>
#include <string>
#include <variant>
#include <vector>
#include "android-base/logging.h"
#include "android/dlext.h"
#include "log/log.h"
#include "nativebridge/native_bridge.h"
#include "utils.h"
namespace android {
@ -31,34 +32,40 @@ namespace android {
// x86). Instances of this class are managed by LibraryNamespaces object.
struct NativeLoaderNamespace {
public:
NativeLoaderNamespace() : android_ns_(nullptr), native_bridge_ns_(nullptr) {}
explicit NativeLoaderNamespace(android_namespace_t* ns)
: android_ns_(ns), native_bridge_ns_(nullptr) {}
explicit NativeLoaderNamespace(native_bridge_namespace_t* ns)
: android_ns_(nullptr), native_bridge_ns_(ns) {}
// TODO(return with errors)
static NativeLoaderNamespace Create(const std::string& name, const std::string& search_paths,
const std::string& permitted_paths,
const NativeLoaderNamespace* parent, bool is_shared,
bool is_greylist_enabled);
NativeLoaderNamespace(NativeLoaderNamespace&&) = default;
NativeLoaderNamespace(const NativeLoaderNamespace&) = default;
NativeLoaderNamespace& operator=(const NativeLoaderNamespace&) = default;
android_namespace_t* get_android_ns() const {
CHECK(native_bridge_ns_ == nullptr);
return android_ns_;
android_namespace_t* ToRawAndroidNamespace() const { return std::get<0>(raw_); }
native_bridge_namespace_t* ToRawNativeBridgeNamespace() const { return std::get<1>(raw_); }
std::string name() const { return name_; }
bool IsBridged() const { return raw_.index() == 1; }
bool IsNil() const {
return IsBridged() ? std::get<1>(raw_) == nullptr : std::get<0>(raw_) == nullptr;
}
native_bridge_namespace_t* get_native_bridge_ns() const {
CHECK(android_ns_ == nullptr);
return native_bridge_ns_;
}
bool Link(const NativeLoaderNamespace& target, const std::string& shared_libs) const;
void* Load(const std::string& lib_name) const;
char* GetError() const;
bool is_android_namespace() const { return native_bridge_ns_ == nullptr; }
static NativeLoaderNamespace GetExportedNamespace(const std::string& name, bool is_bridged);
static NativeLoaderNamespace GetPlatformNamespace(bool is_bridged);
private:
// Only one of them can be not null
android_namespace_t* android_ns_;
native_bridge_namespace_t* native_bridge_ns_;
explicit NativeLoaderNamespace(const std::string& name, android_namespace_t* ns)
: name_(name), raw_(ns) {}
explicit NativeLoaderNamespace(const std::string& name, native_bridge_namespace_t* ns)
: name_(name), raw_(ns) {}
std::string name_;
std::variant<android_namespace_t*, native_bridge_namespace_t*> raw_;
};
} // namespace android