linker: Allow caller to specify parent namespace

This change enables apps to share libraries opened
with RTLD_GLOBAL between different classloader namespaces.

The new parameter to create_namespace allows native_loader
to instruct the linker to share libraries belonging to
global group from a specified namespace instead of
using the caller_ns.

Bug: http://b/28560538
Bug: https://code.google.com/p/android/issues/detail?id=208458
Change-Id: I5d0c62730bbed19cdeb16c7559c74aa262a2475f
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index 2fc8af0..743b01d 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -147,15 +147,22 @@
   return success;
 }
 
-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) {
+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,
+                                              android_namespace_t* parent_namespace) {
   void* caller_addr = __builtin_return_address(0);
   ScopedPthreadMutexLocker locker(&g_dl_mutex);
 
-  android_namespace_t* result = create_namespace(caller_addr, name, ld_library_path,
-                                                 default_library_path, type,
-                                                 permitted_when_isolated_path);
+  android_namespace_t* result = create_namespace(caller_addr,
+                                                 name,
+                                                 ld_library_path,
+                                                 default_library_path,
+                                                 type,
+                                                 permitted_when_isolated_path,
+                                                 parent_namespace);
 
   if (result == nullptr) {
     __bionic_format_dlerror("android_create_namespace failed", linker_get_error_buffer());
diff --git a/linker/linker.cpp b/linker/linker.cpp
index edaa6f4..0e04597 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -138,6 +138,7 @@
 };
 
 android_namespace_t g_default_namespace;
+
 static std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map;
 static android_namespace_t* g_anonymous_namespace = &g_default_namespace;
 
@@ -1887,6 +1888,26 @@
   return global_group;
 }
 
+// This function provides a list of libraries to be shared
+// by the namespace. For the default namespace this is the global
+// group (see make_global_group). For all others this is a group
+// of RTLD_GLOBAL libraries (which includes the global group from
+// the default namespace).
+static soinfo::soinfo_list_t get_shared_group(android_namespace_t* ns) {
+  if (ns == &g_default_namespace) {
+    return make_global_group(ns);
+  }
+
+  soinfo::soinfo_list_t shared_group;
+  ns->soinfo_list().for_each([&](soinfo* si) {
+    if ((si->get_rtld_flags() & RTLD_GLOBAL) != 0) {
+      shared_group.push_back(si);
+    }
+  });
+
+  return shared_group;
+}
+
 static void shuffle(std::vector<LoadTask*>* v) {
   for (size_t i = 0, size = v->size(); i < size; ++i) {
     size_t n = size - i;
@@ -2432,7 +2453,7 @@
   // is still pointing to the default one.
   android_namespace_t* anon_ns =
       create_namespace(nullptr, "(anonymous)", nullptr, anon_ns_library_path,
-                       ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
+                       ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
 
   if (anon_ns == nullptr) {
     g_public_namespace_initialized = false;
@@ -2448,7 +2469,8 @@
                                       const char* ld_library_path,
                                       const char* default_library_path,
                                       uint64_t type,
-                                      const char* permitted_when_isolated_path) {
+                                      const char* permitted_when_isolated_path,
+                                      android_namespace_t* parent_namespace) {
   if (!g_public_namespace_initialized) {
     DL_ERR("cannot create namespace: public namespace is not initialized.");
     return nullptr;
@@ -2460,6 +2482,11 @@
                                    caller_soinfo->get_primary_namespace() :
                                    g_anonymous_namespace;
 
+  // if parent_namespace is nullptr -> set it to the caller namespace
+  if (parent_namespace == nullptr) {
+    parent_namespace = caller_ns;
+  }
+
   ProtectedDataGuard guard;
   std::vector<std::string> ld_library_paths;
   std::vector<std::string> default_library_paths;
@@ -2477,11 +2504,11 @@
   ns->set_permitted_paths(std::move(permitted_paths));
 
   if ((type & ANDROID_NAMESPACE_TYPE_SHARED) != 0) {
-    // If shared - clone the caller namespace
-    ns->add_soinfos(caller_ns->soinfo_list());
+    // If shared - clone the parent namespace
+    ns->add_soinfos(parent_namespace->soinfo_list());
   } else {
-    // If not shared - copy only the global group
-    ns->add_soinfos(make_global_group(caller_ns));
+    // If not shared - copy only the shared group
+    ns->add_soinfos(get_shared_group(parent_namespace));
   }
 
   return ns;
diff --git a/linker/linker.h b/linker/linker.h
index 1613398..6eab00c 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -497,8 +497,12 @@
 };
 
 bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path);
-android_namespace_t* create_namespace(const void* caller_addr, const char* name,
-                                      const char* ld_library_path, const char* default_library_path,
-                                      uint64_t type, const char* permitted_when_isolated_path);
+android_namespace_t* create_namespace(const void* caller_addr,
+                                      const char* name,
+                                      const char* ld_library_path,
+                                      const char* default_library_path,
+                                      uint64_t type,
+                                      const char* permitted_when_isolated_path,
+                                      android_namespace_t* parent_namespace);
 
 #endif
diff --git a/tests/dlext_private.h b/tests/dlext_private.h
index 8eb86ca..049db91 100644
--- a/tests/dlext_private.h
+++ b/tests/dlext_private.h
@@ -83,7 +83,8 @@
                                                             const char* ld_library_path,
                                                             const char* default_library_path,
                                                             uint64_t type,
-                                                            const char* permitted_when_isolated_path);
+                                                            const char* permitted_when_isolated_path,
+                                                            android_namespace_t* parent);
 
 extern void android_set_application_target_sdk_version(uint32_t target);
 
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 109bab5..f5b5e9c 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -652,13 +652,13 @@
   android_namespace_t* ns1 =
           android_create_namespace("private", nullptr,
                                    (lib_path + "/private_namespace_libs").c_str(),
-                                   ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
+                                   ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
   ASSERT_TRUE(ns1 != nullptr) << dlerror();
 
   android_namespace_t* ns2 =
           android_create_namespace("private_isolated", nullptr,
                                    (lib_path + "/private_namespace_libs").c_str(),
-                                   ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr);
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr, nullptr);
   ASSERT_TRUE(ns2 != nullptr) << dlerror();
 
   // This should not have affect search path for default namespace:
@@ -760,19 +760,25 @@
   android_namespace_t* ns_not_isolated =
           android_create_namespace("private", nullptr,
                                    (lib_path + "/private_namespace_libs").c_str(),
-                                   ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
+                                   ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
   ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror();
 
   android_namespace_t* ns_isolated =
-          android_create_namespace("private_isolated1", nullptr,
+          android_create_namespace("private_isolated1",
+                                   nullptr,
                                    (lib_path + "/private_namespace_libs").c_str(),
-                                   ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr);
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
   ASSERT_TRUE(ns_isolated != nullptr) << dlerror();
 
   android_namespace_t* ns_isolated2 =
           android_create_namespace("private_isolated2",
                                    (lib_path + "/private_namespace_libs").c_str(),
-                                   nullptr, ANDROID_NAMESPACE_TYPE_ISOLATED, lib_path.c_str());
+                                   nullptr,
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   lib_path.c_str(),
+                                   nullptr);
   ASSERT_TRUE(ns_isolated2 != nullptr) << dlerror();
 
   ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
@@ -867,14 +873,14 @@
   android_namespace_t* ns_not_isolated =
           android_create_namespace("private", nullptr,
                                    (lib_path + "/private_namespace_libs").c_str(),
-                                   ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
+                                   ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
   ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror();
 
   android_namespace_t* ns_isolated_shared =
           android_create_namespace("private_isolated_shared", nullptr,
                                    (lib_path + "/private_namespace_libs").c_str(),
                                    ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED,
-                                   nullptr);
+                                   nullptr, nullptr);
   ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror();
 
   ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
@@ -981,7 +987,7 @@
           android_create_namespace("private_isolated_shared", nullptr,
                                    (lib_path + "/private_namespace_libs").c_str(),
                                    ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED,
-                                   nullptr);
+                                   nullptr, nullptr);
   ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror();
 
   // Check if "libnstest_dlopened.so" is loaded (and the same)
@@ -1026,6 +1032,78 @@
       << "Error: " << g_public_lib << " is accessible in shared namespace";
 }
 
+TEST(dlext, ns_isolated_rtld_global) {
+  static const char* root_lib = "libnstest_root.so";
+  std::string path = "libc.so:libc++.so:libdl.so:libm.so";
+
+  ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr));
+
+  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH;
+
+  const std::string lib_public_path = lib_path + "/public_namespace_libs";
+
+  android_namespace_t* ns1 =
+          android_create_namespace("isolated1",
+                                   nullptr,
+                                   (lib_path + "/private_namespace_libs").c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   lib_public_path.c_str(),
+                                   nullptr);
+  ASSERT_TRUE(ns1 != nullptr) << dlerror();
+
+  android_namespace_t* ns2 =
+          android_create_namespace("isolated2",
+                                   nullptr,
+                                   (lib_path + "/private_namespace_libs").c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   lib_public_path.c_str(),
+                                   nullptr);
+  ASSERT_TRUE(ns2 != nullptr) << dlerror();
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns1;
+
+  void* handle_global = android_dlopen_ext((lib_public_path + "/" + g_public_lib).c_str(),
+                                           RTLD_GLOBAL,
+                                           &extinfo);
+
+  ASSERT_TRUE(handle_global != nullptr) << dlerror();
+
+  android_namespace_t* ns1_child =
+        android_create_namespace("isolated1_child",
+                                 nullptr,
+                                 (lib_path + "/private_namespace_libs").c_str(),
+                                 ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                 nullptr,
+                                 ns1);
+
+  // Now - only ns1 and ns1 child should be able to dlopen root_lib
+  // attempt to use ns2 should result in dlerror()
+
+  // Check ns1_child first.
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns1_child;
+
+  void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+  // now ns1
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns1;
+
+  handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+  // and ns2 should fail
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns2;
+
+  handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle1 == nullptr);
+  ASSERT_STREQ("dlopen failed: library \"libnstest_public.so\" not found", dlerror());
+}
+
 TEST(dlext, ns_anonymous) {
   static const char* root_lib = "libnstest_root.so";
   std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
@@ -1043,7 +1121,7 @@
   android_namespace_t* ns = android_create_namespace(
                                 "private", nullptr,
                                 (lib_path + "/private_namespace_libs").c_str(),
-                                ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
+                                ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr);
 
   ASSERT_TRUE(ns != nullptr) << dlerror();