Fix logic in loading dependencies crossing namespace boundaries

This change addresses multiple problems introduced by
02586a2a34e6acfccf359b94db840f422b6c0231

1. In the case of unsuccessful dlopen the failure guard is triggered
for two namespaces which leads to double unload.

2. In the case where load_tasks includes libraries from 3 and more
namespaces it results in incorrect linking of libraries shared between
second and third/forth and so on namespaces.

The root cause of these problems was recursive call to find_libraries.
It does not do what it is expected to do. It does not form new load_tasks
list and immediately jumps to linking local_group. Not only this skips
reference counting it also will include unlinked but accessible library
from third (and fourth and fifth) namespaces in invalid local group. The
best case scenario here is that for 3 or more namesapces this will
fail to link. The worse case scenario it will link the library
incorrectly with will lead to very hard to catch bugs.

This change removes recursive call and replaces it with explicit list of
local_groups which should be linked. It also revisits the way we do
reference counting - with this change the reference counts are updated after
after libraries are successfully loaded.

Also update soinfo_free to abort in case when linker tries to free same
soinfo for the second time - this makes linker behavior less undefined.

Test: bionic-unit-tests
Bug: http://b/69787209
Change-Id: Iea25ced181a98c6503cce6e2b832c91d697342d5
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 249d341..04b83f2 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -1051,6 +1051,87 @@
             "\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror());
 }
 
+TEST(dlext, ns_unload_between_namespaces_missing_symbol_direct) {
+  ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr));
+
+  const std::string public_ns_search_path =  get_testlib_root() + "/public_namespace_libs";
+  const std::string private_ns_search_path = get_testlib_root() + "/private_namespace_libs";
+
+  android_namespace_t* ns_public =
+          android_create_namespace("public",
+                                   nullptr,
+                                   public_ns_search_path.c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
+
+  ASSERT_TRUE(android_link_namespaces(ns_public, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+  android_namespace_t* ns_private =
+          android_create_namespace("private",
+                                   nullptr,
+                                   private_ns_search_path.c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
+
+  ASSERT_TRUE(android_link_namespaces(ns_private, ns_public, "libtest_missing_symbol.so")) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns_private, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns_private;
+
+  void* handle = android_dlopen_ext((public_ns_search_path + "/libtest_missing_symbol.so").c_str(),
+                                    RTLD_NOW,
+                                    &extinfo);
+  ASSERT_TRUE(handle == nullptr);
+  ASSERT_EQ(std::string("dlopen failed: cannot locate symbol \"dlopen_testlib_missing_symbol\" referenced by \"") +
+            public_ns_search_path + "/libtest_missing_symbol.so\"...",
+            dlerror());
+}
+
+TEST(dlext, ns_unload_between_namespaces_missing_symbol_indirect) {
+  ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr));
+
+  const std::string public_ns_search_path =  get_testlib_root() + "/public_namespace_libs";
+  const std::string private_ns_search_path = get_testlib_root() + "/private_namespace_libs";
+
+  android_namespace_t* ns_public =
+          android_create_namespace("public",
+                                   nullptr,
+                                   public_ns_search_path.c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
+
+  ASSERT_TRUE(android_link_namespaces(ns_public, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+  android_namespace_t* ns_private =
+          android_create_namespace("private",
+                                   nullptr,
+                                   private_ns_search_path.c_str(),
+                                   ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                   nullptr,
+                                   nullptr);
+
+  ASSERT_TRUE(android_link_namespaces(ns_private,
+                                      ns_public,
+                                      "libnstest_public.so:libtest_missing_symbol_child_public.so")
+              ) << dlerror();
+  ASSERT_TRUE(android_link_namespaces(ns_private, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+  extinfo.library_namespace = ns_private;
+
+  void* handle = android_dlopen_ext("libtest_missing_symbol_root.so", RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle == nullptr);
+  ASSERT_EQ(std::string("dlopen failed: cannot locate symbol \"dlopen_testlib_missing_symbol\" referenced by \"") +
+            private_ns_search_path + "/libtest_missing_symbol_root.so\"...",
+            dlerror());
+}
+
 TEST(dlext, ns_greylist_enabled) {
   ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr));
 
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 8d0271a..e45eb6e 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -113,6 +113,62 @@
 }
 
 // -----------------------------------------------------------------------------
+// Library used by dlext direct unload on the namespace boundary tests
+// -----------------------------------------------------------------------------
+cc_test_library {
+    name: "libtest_missing_symbol",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["dlopen_testlib_missing_symbol.cpp"],
+    allow_undefined_symbols: true,
+    relative_install_path: "bionic-loader-test-libs/public_namespace_libs",
+}
+
+
+// -----------------------------------------------------------------------------
+// Library used by dlext indirect unload on the namespace boundary tests
+//
+// These libraries produce following dependency graph:
+// libtest_missing_symbol_root (private ns)
+// +-> libbnstest_public (public ns)
+// +-> libtest_missing_symbol_child_public (public ns)
+//     +-> libnstest_public (public ns)
+// +-> libtest_missing_symbol_child_private (private_ns)
+//     +-> libnstest_public (public_ns)
+//
+// All libraries except libtest_missing_symbol are located in
+// private_namespace_libs/
+// -----------------------------------------------------------------------------
+cc_test_library {
+    name: "libtest_missing_symbol_child_public",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["empty.cpp"],
+    relative_install_path: "bionic-loader-test-libs/public_namespace_libs",
+    shared_libs: ["libnstest_public"],
+}
+
+cc_test_library {
+    name: "libtest_missing_symbol_child_private",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["empty.cpp"],
+    relative_install_path: "bionic-loader-test-libs/private_namespace_libs",
+    shared_libs: ["libnstest_public"],
+}
+
+cc_test_library {
+    name: "libtest_missing_symbol_root",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["dlopen_testlib_missing_symbol.cpp"],
+    relative_install_path: "bionic-loader-test-libs/private_namespace_libs",
+    allow_undefined_symbols: true,
+    shared_libs: [
+        "libnstest_public",
+        "libtest_missing_symbol_child_public",
+        "libtest_missing_symbol_child_private",
+    ],
+}
+
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
 // Library used by dlfcn nodelete tests
 // -----------------------------------------------------------------------------
 cc_test_library {
@@ -142,8 +198,65 @@
 
 // -----------------------------------------------------------------------------
 // Build test helper libraries for linker namespaces
+//
+// This set of libraries is used to verify linker namespaces.
+//
+// Test cases
+// 1. Check that private libraries loaded in different namespaces are
+//    different. Check that dlsym does not confuse them.
+// 2. Check that public libraries loaded in different namespaces are shared
+//    between them.
+// 3. Check that namespace sticks on dlopen
+// 4. Check that having access to shared library (libnstest_public.so)
+//    does not expose symbols from dependent library (libnstest_public_internal.so)
+//
+// Dependency tree (visibility)
+// libnstest_root.so (this should be local to the namespace)
+// +-> libnstest_public.so
+//     +-> libnstest_public_internal.so
+// +-> libnstest_private.so
+//
+// libnstest_dlopened.so (library in private namespace dlopened from libnstest_root.so)
 // -----------------------------------------------------------------------------
-// include $(LOCAL_PATH)/Android.build.linker_namespaces.mk
+cc_test_library {
+    name: "libnstest_root",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["namespaces_root.cpp"],
+    relative_install_path: "bionic-loader-test-libs/private_namespace_libs",
+    shared_libs: [
+        "libnstest_public",
+        "libnstest_private",
+    ],
+}
+
+cc_test_library {
+    name: "libnstest_public_internal",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["namespaces_public_internal.cpp"],
+    relative_install_path: "bionic-loader-test-libs/public_namespace_libs",
+}
+
+cc_test_library {
+    name: "libnstest_public",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["namespaces_public.cpp"],
+    relative_install_path: "bionic-loader-test-libs/public_namespace_libs",
+    shared_libs: ["libnstest_public_internal"],
+}
+
+cc_test_library {
+    name: "libnstest_private",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["namespaces_private.cpp"],
+    relative_install_path: "bionic-loader-test-libs/private_namespace_libs",
+}
+
+cc_test_library {
+    name: "libnstest_dlopened",
+    defaults: ["bionic_testlib_defaults"],
+    srcs: ["namespaces_dlopened.cpp"],
+    relative_install_path: "bionic-loader-test-libs/private_namespace_libs",
+}
 
 // -----------------------------------------------------------------------------
 // Build DT_RUNPATH test helper libraries
diff --git a/tests/libs/Android.build.linker_namespaces.mk b/tests/libs/Android.build.linker_namespaces.mk
index cd9d7f1..e2b01a7 100644
--- a/tests/libs/Android.build.linker_namespaces.mk
+++ b/tests/libs/Android.build.linker_namespaces.mk
@@ -19,51 +19,6 @@
 # -----------------------------------------------------------------------------
 
 # -----------------------------------------------------------------------------
-# Test cases
-# 1. Check that private libraries loaded in different namespaces are
-#    different. Check that dlsym does not confuse them.
-# 2. Check that public libraries loaded in different namespaces are shared
-#    between them.
-# 3. Check that namespace sticks on dlopen
-# 4. Check that having access to shared library (libnstest_public.so)
-#    does not expose symbols from dependent library (libnstest_public_internal.so)
-#
-# Dependency tree (visibility)
-# libnstest_root.so (this should be local to the namespace)
-# +-> libnstest_public.so
-#     +-> libnstest_public_internal.so
-# +-> libnstest_private.so
-#
-# libnstest_dlopened.so (library in private namespace dlopened from libnstest_root.so)
-# -----------------------------------------------------------------------------
-libnstest_root_src_files := namespaces_root.cpp
-libnstest_root_shared_libraries := libnstest_public libnstest_private
-libnstest_root_relative_install_path := private_namespace_libs
-module := libnstest_root
-include $(LOCAL_PATH)/Android.build.testlib.target.mk
-
-libnstest_public_internal_src_files := namespaces_public_internal.cpp
-module := libnstest_public_internal
-libnstest_public_internal_relative_install_path := public_namespace_libs
-include $(LOCAL_PATH)/Android.build.testlib.target.mk
-
-libnstest_public_src_files := namespaces_public.cpp
-libnstest_public_shared_libraries := libnstest_public_internal
-module := libnstest_public
-libnstest_public_relative_install_path := public_namespace_libs
-include $(LOCAL_PATH)/Android.build.testlib.target.mk
-
-libnstest_private_src_files := namespaces_private.cpp
-libnstest_private_relative_install_path := private_namespace_libs
-module := libnstest_private
-include $(LOCAL_PATH)/Android.build.testlib.target.mk
-
-libnstest_dlopened_src_files := namespaces_dlopened.cpp
-libnstest_dlopened_relative_install_path := private_namespace_libs
-module := libnstest_dlopened
-include $(LOCAL_PATH)/Android.build.testlib.target.mk
-
-# -----------------------------------------------------------------------------
 # This set of libraries is to test isolated namespaces
 #
 # Isolated namespaces do not allow loading of the library outside of
diff --git a/tests/libs/dlopen_testlib_missing_symbol.cpp b/tests/libs/dlopen_testlib_missing_symbol.cpp
new file mode 100644
index 0000000..0f73c60
--- /dev/null
+++ b/tests/libs/dlopen_testlib_missing_symbol.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+extern "C" void dlopen_testlib_missing_symbol();
+
+extern "C" bool dlopen_testlib_simple_func() {
+  dlopen_testlib_missing_symbol();
+  return true;
+}