Skip to content

Commit

Permalink
Make classloader namespace names unique, for debugging.
Browse files Browse the repository at this point in the history
Also take the opportunity to switch to shorter names, when they are
changing anyway. These namespaces aren't visible, so renaming them
shouldn't be a compat issue.

Test: atest libnativeloader_test libnativeloader_e2e_tests
Test: adb shell "setprop debug.ld.all dlopen,dlerror && setprop log.tag.nativeloader :v && stop && start"
  Then check logcat that the classloader namespaces get the expected
  unique names (per process).
Bug: 258340826
Change-Id: Ic5d6ba850d8cd9cdfb3a66e175dfc87046c0ba31
  • Loading branch information
marstj authored and Treehugger Robot committed Nov 18, 2022
1 parent 51d5e87 commit e5f1d63
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 28 deletions.
10 changes: 5 additions & 5 deletions libnativeloader/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ by the platform.

The most typical use case of this library is calling `System.loadLibrary(name)`.
When the method is called, the ART runtime delegates the call to this library
along with the reference to the classloader where the call was made. Then this
library finds the linker namespace (named `classloader-namespace`) that is
associated with the given classloader, and tries to load the requested library
from the namespace. The actual searching, loading, and linking of the library
is performed by the dynamic linker.
along with the reference to the classloader where the call was made. Then this
library finds the linker namespace (typically with the name `clns-` followed by
a number to make it unique) that is associated with the given classloader, and
tries to load the requested library from that namespace. The actual searching,
loading, and linking of the library is performed by the dynamic linker.

The linker namespace is created when an APK is loaded into the process, and is
associated with the classloader that loaded the APK. The linker namespace is
Expand Down
33 changes: 20 additions & 13 deletions libnativeloader/library_namespaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <android-base/macros.h>
#include <android-base/result.h>
#include <android-base/strings.h>
#include <android-base/stringprintf.h>
#include <nativehelper/scoped_utf_chars.h>

#include "nativeloader/dlext_namespaces.h"
Expand All @@ -55,19 +56,20 @@ constexpr const char* kVndkNamespaceName = "vndk";
// vndk_product namespace for unbundled product apps
constexpr const char* kVndkProductNamespaceName = "vndk_product";

// classloader-namespace is a linker namespace that is created for the loaded
// app. To be specific, it is created for the app classloader. When
// System.load() is called from a Java class that is loaded from the
// classloader, the classloader-namespace namespace associated with that
// classloader is selected for dlopen. The namespace is configured so that its
// search path is set to the app-local JNI directory and it is linked to the
// system namespace with the names of libs listed in the public.libraries.txt.
// This way an app can only load its own JNI libraries along with the public libs.
constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
// Same thing for unbundled vendor APKs.
constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
// Same thing for unbundled product APKs.
constexpr const char* kProductClassloaderNamespaceName = "product-classloader-namespace";
// clns-XX is a linker namespace that is created for normal apps installed in
// the data partition. To be specific, it is created for the app classloader.
// When System.load() is called from a Java class that is loaded from the
// classloader, the clns namespace associated with that classloader is selected
// for dlopen. The namespace is configured so that its search path is set to the
// app-local JNI directory and it is linked to the system namespace with the
// names of libs listed in the public.libraries.txt and other public libraries.
// This way an app can only load its own JNI libraries along with the public
// libs.
constexpr const char* kClassloaderNamespaceName = "clns";
// Same thing for unbundled APKs in the vendor partition.
constexpr const char* kVendorClassloaderNamespaceName = "vendor-clns";
// Same thing for unbundled APKs in the product partition.
constexpr const char* kProductClassloaderNamespaceName = "product-clns";
// If the namespace is shared then add this suffix to help identify it in debug
// messages. A shared namespace (cf. ANDROID_NAMESPACE_TYPE_SHARED) has
// inherited all the libraries of the parent classloader namespace, or the
Expand Down Expand Up @@ -294,6 +296,11 @@ Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t t
namespace_name = namespace_name + kSharedNamespaceSuffix;
}

// Append a unique number to the namespace name, to tell them apart when
// debugging linker issues, e.g. with debug.ld.all set to "dlopen,dlerror".
static int clns_count = 0;
namespace_name = android::base::StringPrintf("%s-%d", namespace_name.c_str(), ++clns_count);

ALOGD(
"Configuring %s for %s %s. target_sdk_version=%u, uses_libraries=%s, library_path=%s, "
"permitted_path=%s",
Expand Down
21 changes: 11 additions & 10 deletions libnativeloader/native_loader_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ namespace nativeloader {

using ::testing::Eq;
using ::testing::NotNull;
using ::testing::StartsWith;
using ::testing::StrEq;
using internal::ConfigEntry; // NOLINT - ConfigEntry is actually used
using internal::ParseApexLibrariesConfig;
Expand Down Expand Up @@ -191,7 +192,7 @@ class NativeLoaderTest_Create : public NativeLoaderTest {
std::string permitted_path = "/data/app/foo/" LIB_DIR;

// expected output (.. for the default test inputs)
std::string expected_namespace_name = "classloader-namespace";
std::string expected_namespace_prefix = "clns";
uint64_t expected_namespace_flags =
ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
std::string expected_library_path = library_path;
Expand Down Expand Up @@ -227,7 +228,7 @@ class NativeLoaderTest_Create : public NativeLoaderTest {
EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(testing::AnyNumber());

EXPECT_CALL(*mock, mock_create_namespace(
Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
Eq(IsBridged()), StartsWith(expected_namespace_prefix + "-"), nullptr,
StrEq(expected_library_path), expected_namespace_flags,
StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str())))
.WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str()))));
Expand Down Expand Up @@ -322,7 +323,7 @@ TEST_P(NativeLoaderTest_Create, BundledSystemApp) {
dex_path = "/system/app/foo/foo.apk";
is_shared = true;

expected_namespace_name = "classloader-namespace-shared";
expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
Expand All @@ -332,7 +333,7 @@ TEST_P(NativeLoaderTest_Create, BundledVendorApp) {
dex_path = "/vendor/app/foo/foo.apk";
is_shared = true;

expected_namespace_name = "classloader-namespace-shared";
expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
Expand All @@ -342,7 +343,7 @@ TEST_P(NativeLoaderTest_Create, UnbundledVendorApp) {
dex_path = "/vendor/app/foo/foo.apk";
is_shared = false;

expected_namespace_name = "vendor-classloader-namespace";
expected_namespace_prefix = "vendor-clns";
expected_library_path = expected_library_path + ":/vendor/" LIB_DIR;
expected_permitted_path = expected_permitted_path + ":/vendor/" LIB_DIR;
expected_shared_libs_to_platform_ns =
Expand All @@ -356,7 +357,7 @@ TEST_P(NativeLoaderTest_Create, BundledProductApp) {
dex_path = "/product/app/foo/foo.apk";
is_shared = true;

expected_namespace_name = "classloader-namespace-shared";
expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
Expand All @@ -366,7 +367,7 @@ TEST_P(NativeLoaderTest_Create, SystemServerWithApexJars) {
dex_path = "/system/framework/services.jar:/apex/com.android.conscrypt/javalib/service-foo.jar";
is_shared = true;

expected_namespace_name = "classloader-namespace-shared";
expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
expected_link_with_conscrypt_ns = true;
SetExpectations();
Expand All @@ -378,7 +379,7 @@ TEST_P(NativeLoaderTest_Create, UnbundledProductApp) {
is_shared = false;

if (is_product_vndk_version_defined()) {
expected_namespace_name = "product-classloader-namespace";
expected_namespace_prefix = "product-clns";
expected_library_path = expected_library_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
expected_permitted_path =
expected_permitted_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
Expand Down Expand Up @@ -416,7 +417,7 @@ TEST_P(NativeLoaderTest_Create, TwoApks) {
const std::string second_app_permitted_path = "/data/app/bar/" LIB_DIR;
const std::string expected_second_app_permitted_path =
std::string("/data:/mnt/expand:") + second_app_permitted_path;
const std::string expected_second_app_parent_namespace = "classloader-namespace";
const std::string expected_second_app_parent_namespace = "clns";
// no ALSO_USED_AS_ANONYMOUS
const uint64_t expected_second_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;

Expand All @@ -429,7 +430,7 @@ TEST_P(NativeLoaderTest_Create, TwoApks) {
// namespace for the second app is created. Its parent is set to the namespace
// of the first app.
EXPECT_CALL(*mock, mock_create_namespace(
Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
Eq(IsBridged()), StartsWith(expected_namespace_prefix + "-"), nullptr,
StrEq(second_app_library_path), expected_second_namespace_flags,
StrEq(expected_second_app_permitted_path), NsEq(dex_path.c_str())))
.WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str()))));
Expand Down

0 comments on commit e5f1d63

Please sign in to comment.