linker: implement shared namespaces

Shared namespaces clone the list of loaded native
libraries from the caller namespace. This allows
classloaders for bundled apps to share already loaded
libraries with default namespace.

Bug: http://b/22548808
Bug: http://b/26165097
Change-Id: I8949d45937fdb38e1f586ff0679003adac0d9dad
diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h
index 7979c43..d5ec386 100644
--- a/libc/include/android/dlext.h
+++ b/libc/include/android/dlext.h
@@ -142,6 +142,33 @@
 extern bool android_init_namespaces(const char* public_ns_sonames,
                                     const char* anon_ns_library_path);
 
+
+enum {
+  /* A regular namespace is the namespace with a custom search path that does
+   * not impose any restrictions on the location of native libraries.
+   */
+  ANDROID_NAMESPACE_TYPE_REGULAR = 0,
+
+  /* An isolated namespace requires all the libraries to be on the search path
+   * or under permitted_when_isolated_path. The search path is the union of
+   * ld_library_path and default_library_path.
+   */
+  ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
+
+  /* The shared namespace clones the list of libraries of the caller namespace upon creation
+   * which means that they are shared between namespaces - the caller namespace and the new one
+   * will use the same copy of a library if it was loaded prior to android_create_namespace call.
+   *
+   * Note that libraries loaded after the namespace is created will not be shared.
+   *
+   * Shared namespaces can be isolated or regular. Note that they do not inherit the search path nor
+   * permitted_path from the caller's namespace.
+   */
+  ANDROID_NAMESPACE_TYPE_SHARED = 2,
+  ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED = ANDROID_NAMESPACE_TYPE_SHARED |
+                                           ANDROID_NAMESPACE_TYPE_ISOLATED,
+};
+
 /*
  * Creates new linker namespace.
  * ld_library_path and default_library_path represent the search path
@@ -152,19 +179,19 @@
  * 2. In directories specified by DT_RUNPATH of the "needed by" binary.
  * 3. deault_library_path (This of this as namespace-local default library path)
  *
- * When is_isolated is true the resulting namespace requires all of the libraries
- * to be on the search path or under the permitted_when_isolated_path; the search_path is
- * ld_library_path:default_library_path. Note that the permitted_when_isolated_path path
- * is not part of the search_path and does not affect the search order. It is a way
- * to allow loading libraries from specific locations when using absolute path.
- *
+ * When type is ANDROID_NAMESPACE_TYPE_ISOLATED the resulting namespace requires all of
+ * the libraries to be on the search path or under the permitted_when_isolated_path;
+ * the search_path is ld_library_path:default_library_path. Note that the
+ * permitted_when_isolated_path path is not part of the search_path and
+ * does not affect the search order. It is a way to allow loading libraries from specific
+ * locations when using absolute path.
  * If a library or any of its dependencies are outside of the permitted_when_isolated_path
  * and search_path, and it is not part of the public namespace dlopen will fail.
  */
 extern struct android_namespace_t* android_create_namespace(const char* name,
                                                             const char* ld_library_path,
                                                             const char* default_library_path,
-                                                            bool is_isolated,
+                                                            uint64_t type,
                                                             const char* permitted_when_isolated_path);
 
 __END_DECLS
diff --git a/libdl/libdl.c b/libdl/libdl.c
index 0604d3e..fa5237f 100644
--- a/libdl/libdl.c
+++ b/libdl/libdl.c
@@ -65,7 +65,7 @@
 struct android_namespace_t* android_create_namespace(const char* name __unused,
                                                      const char* ld_library_path __unused,
                                                      const char* default_library_path __unused,
-                                                     bool isolated __unused,
+                                                     uint64_t type __unused,
                                                      const char* permitted_when_isolated_path __unused) {
   return 0;
 }
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index ba54d39..a7c3fb0 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -148,12 +148,14 @@
 }
 
 android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
-                                              const char* default_library_path, bool is_isolated,
+                                              const char* default_library_path, uint64_t type,
                                               const char* permitted_when_isolated_path) {
+  void* caller_addr = __builtin_return_address(0);
   ScopedPthreadMutexLocker locker(&g_dl_mutex);
 
-  android_namespace_t* result = create_namespace(name, ld_library_path, default_library_path,
-                                                 is_isolated, permitted_when_isolated_path);
+  android_namespace_t* result = create_namespace(caller_addr, name, ld_library_path,
+                                                 default_library_path, type,
+                                                 permitted_when_isolated_path);
 
   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 e38e252..eb938c7 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1747,7 +1747,6 @@
   });
 
   return true;
-
 }
 
 static bool load_library(android_namespace_t* ns,
@@ -2377,8 +2376,12 @@
   g_public_namespace_initialized = true;
 
   // create anonymous namespace
+  // When the caller is nullptr - create_namespace will take global group
+  // from the anonymous namespace, which is fine because anonymous namespace
+  // is still pointing to the default one.
   android_namespace_t* anon_ns =
-      create_namespace("(anonymous)", nullptr, anon_ns_library_path, false, nullptr);
+      create_namespace(nullptr, "(anonymous)", nullptr, anon_ns_library_path,
+                       ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
 
   if (anon_ns == nullptr) {
     g_public_namespace_initialized = false;
@@ -2389,16 +2392,23 @@
   return true;
 }
 
-android_namespace_t* create_namespace(const char* name,
+android_namespace_t* create_namespace(const void* caller_addr,
+                                      const char* name,
                                       const char* ld_library_path,
                                       const char* default_library_path,
-                                      bool is_isolated,
+                                      uint64_t type,
                                       const char* permitted_when_isolated_path) {
   if (!g_public_namespace_initialized) {
     DL_ERR("cannot create namespace: public namespace is not initialized.");
     return nullptr;
   }
 
+  soinfo* caller_soinfo = find_containing_library(caller_addr);
+
+  android_namespace_t* caller_ns = caller_soinfo != nullptr ?
+                                   caller_soinfo->get_namespace() :
+                                   g_anonymous_namespace;
+
   ProtectedDataGuard guard;
   std::vector<std::string> ld_library_paths;
   std::vector<std::string> default_library_paths;
@@ -2410,14 +2420,20 @@
 
   android_namespace_t* ns = new (g_namespace_allocator.alloc()) android_namespace_t();
   ns->set_name(name);
-  ns->set_isolated(is_isolated);
+  ns->set_isolated((type & ANDROID_NAMESPACE_TYPE_ISOLATED) != 0);
   ns->set_ld_library_paths(std::move(ld_library_paths));
   ns->set_default_library_paths(std::move(default_library_paths));
   ns->set_permitted_paths(std::move(permitted_paths));
 
-  // TODO(dimtiry): Should this be global group of caller's namespace?
-  auto global_group = make_global_group(&g_default_namespace);
-  std::copy(global_group.begin(), global_group.end(), std::back_inserter(ns->soinfo_list()));
+  if ((type & ANDROID_NAMESPACE_TYPE_SHARED) != 0) {
+    // If shared - clone the caller namespace
+    auto& soinfo_list = caller_ns->soinfo_list();
+    std::copy(soinfo_list.begin(), soinfo_list.end(), std::back_inserter(ns->soinfo_list()));
+  } else {
+    // If not shared - copy only the global group
+    auto global_group = make_global_group(caller_ns);
+    std::copy(global_group.begin(), global_group.end(), std::back_inserter(ns->soinfo_list()));
+  }
 
   return ns;
 }
diff --git a/linker/linker.h b/linker/linker.h
index 5ec259e..5a06853 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -444,8 +444,8 @@
 uint32_t get_application_target_sdk_version();
 
 bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path);
-android_namespace_t* create_namespace(const char* name, const char* ld_library_path,
-                                      const char* default_library_path, bool is_isolated,
-                                      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);
 
 #endif
diff --git a/linker/linker_debug.h b/linker/linker_debug.h
index 17c6986..5af9929 100644
--- a/linker/linker_debug.h
+++ b/linker/linker_debug.h
@@ -55,6 +55,7 @@
  *********************************************************************/
 
 #include "private/libc_logging.h"
+#include <unistd.h>
 
 __LIBC_HIDDEN__ extern int g_ld_debug_verbosity;
 
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 5327e36..261aa55 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -625,13 +625,21 @@
 
   // Check that libraries added to public namespace are NODELETE
   dlclose(handle_public);
-  handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), RTLD_NOW | RTLD_NOLOAD);
+  handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(),
+                         RTLD_NOW | RTLD_NOLOAD);
+
   ASSERT_TRUE(handle_public != nullptr) << dlerror();
 
-  android_namespace_t* ns1 = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false, nullptr);
+  android_namespace_t* ns1 =
+          android_create_namespace("private", nullptr,
+                                   (lib_path + "/private_namespace_libs").c_str(),
+                                   ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
   ASSERT_TRUE(ns1 != nullptr) << dlerror();
 
-  android_namespace_t* ns2 = android_create_namespace("private_isolated", nullptr, (lib_path + "/private_namespace_libs").c_str(), true, nullptr);
+  android_namespace_t* ns2 =
+          android_create_namespace("private_isolated", nullptr,
+                                   (lib_path + "/private_namespace_libs").c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr);
   ASSERT_TRUE(ns2 != nullptr) << dlerror();
 
   // This should not have affect search path for default namespace:
@@ -732,13 +740,22 @@
 
   ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror();
 
-  android_namespace_t* ns_not_isolated = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false, nullptr);
+  android_namespace_t* ns_not_isolated =
+          android_create_namespace("private", nullptr,
+                                   (lib_path + "/private_namespace_libs").c_str(),
+                                   ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
   ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror();
 
-  android_namespace_t* ns_isolated = android_create_namespace("private_isolated1", nullptr, (lib_path + "/private_namespace_libs").c_str(), true, nullptr);
+  android_namespace_t* ns_isolated =
+          android_create_namespace("private_isolated1", nullptr,
+                                   (lib_path + "/private_namespace_libs").c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED, 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, true, lib_path.c_str());
+  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());
   ASSERT_TRUE(ns_isolated2 != nullptr) << dlerror();
 
   ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
@@ -808,6 +825,122 @@
   dlclose(handle1);
 }
 
+TEST(dlext, ns_shared) {
+  static const char* root_lib = "libnstest_root_not_isolated.so";
+  static const char* root_lib_isolated = "libnstest_root.so";
+  std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
+
+  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH;
+  const std::string lib_public_path = lib_path + "/public_namespace_libs/" + g_public_lib;
+  void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle_public != nullptr) << dlerror();
+
+  android_set_application_target_sdk_version(42U); // something > 23
+
+  ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror();
+
+  // preload this library to the default namespace to check if it
+  // is shared later on.
+  void* handle_dlopened =
+          dlopen((lib_path + "/private_namespace_libs/libnstest_dlopened.so").c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle_dlopened != nullptr) << dlerror();
+
+  android_namespace_t* ns_not_isolated =
+          android_create_namespace("private", nullptr,
+                                   (lib_path + "/private_namespace_libs").c_str(),
+                                   ANDROID_NAMESPACE_TYPE_REGULAR, 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);
+  ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror();
+
+  ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
+  ASSERT_STREQ("dlopen failed: library \"libnstest_root_not_isolated.so\" not found", dlerror());
+
+  std::string lib_private_external_path =
+      lib_path + "/private_namespace_libs_external/libnstest_private_external.so";
+
+  // Load lib_private_external_path to default namespace
+  // (it should remain invisible for the isolated namespaces after this)
+  void* handle = dlopen(lib_private_external_path.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns_not_isolated;
+
+  void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+  extinfo.library_namespace = ns_isolated_shared;
+
+  void* handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 == nullptr);
+  ASSERT_STREQ("dlopen failed: library \"libnstest_private_external.so\" not found", dlerror());
+
+  // Check dlopen by absolute path
+  handle2 = android_dlopen_ext(lib_private_external_path.c_str(), RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle2 == nullptr);
+  ASSERT_EQ("dlopen failed: library \"" + lib_private_external_path + "\" is not accessible for the namespace \"private_isolated_shared\"", dlerror());
+
+  // load libnstest_root.so to shared namespace in order to check that everything is different
+  // except shared libnstest_dlopened.so
+
+  handle2 = android_dlopen_ext(root_lib_isolated, RTLD_NOW, &extinfo);
+
+  typedef const char* (*fn_t)();
+  fn_t ns_get_local_string = reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_local_string"));
+  ASSERT_TRUE(ns_get_local_string != nullptr) << dlerror();
+  fn_t ns_get_local_string_shared = reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_local_string"));
+  ASSERT_TRUE(ns_get_local_string_shared != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is local to root library", ns_get_local_string());
+  ASSERT_STREQ("This string is local to root library", ns_get_local_string_shared());
+  ASSERT_TRUE(ns_get_local_string() != ns_get_local_string_shared());
+
+  fn_t ns_get_private_extern_string =
+          reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_private_extern_string"));
+  ASSERT_TRUE(ns_get_private_extern_string != nullptr) << dlerror();
+  fn_t ns_get_private_extern_string_shared =
+          reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_private_extern_string"));
+  ASSERT_TRUE(ns_get_private_extern_string_shared() != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string());
+  ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string_shared());
+  ASSERT_TRUE(ns_get_private_extern_string() != ns_get_private_extern_string_shared());
+
+  fn_t ns_get_public_extern_string =
+          reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_public_extern_string"));
+  ASSERT_TRUE(ns_get_public_extern_string != nullptr) << dlerror();
+  fn_t ns_get_public_extern_string_shared =
+          reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_public_extern_string"));
+  ASSERT_TRUE(ns_get_public_extern_string_shared != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string());
+  ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string_shared());
+  ASSERT_TRUE(ns_get_public_extern_string() == ns_get_public_extern_string_shared());
+
+  fn_t ns_get_dlopened_string = reinterpret_cast<fn_t>(dlsym(handle1, "ns_get_dlopened_string"));
+  ASSERT_TRUE(ns_get_dlopened_string != nullptr) << dlerror();
+  fn_t ns_get_dlopened_string_shared = reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_dlopened_string"));
+  ASSERT_TRUE(ns_get_dlopened_string_shared != nullptr) << dlerror();
+  const char** ns_dlopened_string = static_cast<const char**>(dlsym(handle_dlopened, "g_private_dlopened_string"));
+  ASSERT_TRUE(ns_dlopened_string != nullptr) << dlerror();
+
+  ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string());
+  ASSERT_STREQ("This string is from private namespace (dlopened library)", *ns_dlopened_string);
+  ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string_shared());
+  ASSERT_TRUE(ns_get_dlopened_string() != ns_get_dlopened_string_shared());
+  ASSERT_TRUE(*ns_dlopened_string == ns_get_dlopened_string_shared());
+
+  dlclose(handle1);
+  dlclose(handle2);
+}
+
 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;
@@ -825,7 +958,7 @@
   android_namespace_t* ns = android_create_namespace(
                                 "private", nullptr,
                                 (lib_path + "/private_namespace_libs").c_str(),
-                                false, nullptr);
+                                ANDROID_NAMESPACE_TYPE_REGULAR, nullptr);
 
   ASSERT_TRUE(ns != nullptr) << dlerror();