Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8537781c authored by Jiyong Park's avatar Jiyong Park
Browse files

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
parent 3afe5f22
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ cc_library {
        android: {
            srcs: [
                "library_namespaces.cpp",
                "native_loader_namespace.cpp",
                "public_libraries.cpp",
            ],
            shared_libs: [
+38 −135
Original line number Diff line number Diff line
@@ -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();
  // 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;
  }

    // 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);
  // ... and link to other namespaces to allow access to some public libraries
  bool is_bridged = app_ns.IsBridged();

    if (!android_link_namespaces(ns, platform_ns, system_exposed_libraries.c_str())) {
      *error_msg = dlerror();
  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 != nullptr) {
      if (!android_link_namespaces(ns, runtime_ns, runtime_exposed_libraries.c_str())) {
        *error_msg = dlerror();
  if (!runtime_ns.IsNil()) {
    if (!app_ns.Link(runtime_ns, runtime_public_libraries())) {
      *error_msg = app_ns.GetError();
      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();
  // 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()) {
      if (!android_link_namespaces(ns, vendor_ns, vendor_public_libraries().c_str())) {
    auto vendor_ns = NativeLoaderNamespace::GetExportedNamespace(kVendorNamespaceName, is_bridged);
    if (!app_ns.Link(vendor_ns, vendor_public_libraries())) {
      *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);
  }

  namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns));
  namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), app_ns));

  return &(namespaces_.back().second);
}
+7 −21
Original line number Diff line number Diff line
@@ -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);
  void* handle = ns->Load(path);
  if (handle == nullptr) {
      *error_msg = strdup(dlerror());
    *error_msg = ns->GetError();
  }
    *needs_native_bridge = false;
  *needs_native_bridge = ns->IsBridged();
  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;
  }
}

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

+128 −0
Original line number Diff line number Diff line
/*
 * 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
+27 −20
Original line number Diff line number Diff line
@@ -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_); }

  native_bridge_namespace_t* get_native_bridge_ns() const {
    CHECK(android_ns_ == nullptr);
    return native_bridge_ns_;
  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;
  }

  bool is_android_namespace() const { return native_bridge_ns_ == nullptr; }
  bool Link(const NativeLoaderNamespace& target, const std::string& shared_libs) const;
  void* Load(const std::string& lib_name) const;
  char* GetError() const;

  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