linker: Add support for "whitelisted" property in linker config files

In order to enable no-vendor-variant VNDK, we need a way to restrict a
namespace to only a list of whitelisted libraries.  We add a new
"whitelisted" property for this.  If the property is not set, all
libraries in the search paths are available in a namespace.  If the
property is set, only the libraries named are available.

Bug: 119423884
Test: Boot with no-vendor-variant VNDK enabled using the new property.
Change-Id: Id808c1733c8e2c2c3462b04c72461f9698403571
diff --git a/linker/ld.config.format.md b/linker/ld.config.format.md
index 686d6be..faf5cc8 100644
--- a/linker/ld.config.format.md
+++ b/linker/ld.config.format.md
@@ -79,5 +79,8 @@
 # and links it to default namespace
 namespace.ns.links = default
 namespace.ns.link.default.shared_libs = libc.so:libdl.so:libm.so:libstdc++.so
+
+# This defines what libraries are allowed to be loaded from ns1
+namespace.ns1.whitelisted = libsomething.so
 ```
 
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 49c8f11..c60ab6a 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -4156,6 +4156,7 @@
     ns->set_isolated(ns_config->isolated());
     ns->set_default_library_paths(ns_config->search_paths());
     ns->set_permitted_paths(ns_config->permitted_paths());
+    ns->set_whitelisted_libs(ns_config->whitelisted_libs());
 
     namespaces[ns_config->name()] = ns;
     if (ns_config->visible()) {
diff --git a/linker/linker_config.cpp b/linker/linker_config.cpp
index f7f9c41..437aa86 100644
--- a/linker/linker_config.cpp
+++ b/linker/linker_config.cpp
@@ -552,6 +552,12 @@
     ns_config->set_isolated(properties.get_bool(property_name_prefix + ".isolated"));
     ns_config->set_visible(properties.get_bool(property_name_prefix + ".visible"));
 
+    std::string whitelisted =
+        properties.get_string(property_name_prefix + ".whitelisted", &lineno);
+    if (!whitelisted.empty()) {
+      ns_config->set_whitelisted_libs(android::base::Split(whitelisted, ":"));
+    }
+
     // these are affected by is_asan flag
     if (is_asan) {
       property_name_prefix += ".asan";
diff --git a/linker/linker_config.h b/linker/linker_config.h
index 49739ee..a318bba 100644
--- a/linker/linker_config.h
+++ b/linker/linker_config.h
@@ -92,6 +92,10 @@
     return permitted_paths_;
   }
 
+  const std::vector<std::string>& whitelisted_libs() const {
+    return whitelisted_libs_;
+  }
+
   const std::vector<NamespaceLinkConfig>& links() const {
     return namespace_links_;
   }
@@ -116,12 +120,17 @@
   void set_permitted_paths(std::vector<std::string>&& permitted_paths) {
     permitted_paths_ = permitted_paths;
   }
+
+  void set_whitelisted_libs(std::vector<std::string>&& whitelisted_libs) {
+    whitelisted_libs_ = whitelisted_libs;
+  }
  private:
   const std::string name_;
   bool isolated_;
   bool visible_;
   std::vector<std::string> search_paths_;
   std::vector<std::string> permitted_paths_;
+  std::vector<std::string> whitelisted_libs_;
   std::vector<NamespaceLinkConfig> namespace_links_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceConfig);
diff --git a/linker/linker_config_test.cpp b/linker/linker_config_test.cpp
index 6a55bb2..4937056 100644
--- a/linker/linker_config_test.cpp
+++ b/linker/linker_config_test.cpp
@@ -56,6 +56,7 @@
   "enable.target.sdk.version = true\n"
   "additional.namespaces=system\n"
   "additional.namespaces+=vndk\n"
+  "additional.namespaces+=vndk_in_system\n"
   "namespace.default.isolated = true\n"
   "namespace.default.search.paths = /vendor/${LIB}\n"
   "namespace.default.permitted.paths = /vendor/${LIB}\n"
@@ -82,6 +83,12 @@
   "namespace.vndk.asan.search.paths += /system/${LIB}/vndk\n"
   "namespace.vndk.links = default\n"
   "namespace.vndk.link.default.allow_all_shared_libs = true\n"
+  "namespace.vndk.link.vndk_in_system.allow_all_shared_libs = true\n"
+  "namespace.vndk_in_system.isolated = true\n"
+  "namespace.vndk_in_system.visible = true\n"
+  "namespace.vndk_in_system.search.paths = /system/${LIB}\n"
+  "namespace.vndk_in_system.permitted.paths = /system/${LIB}\n"
+  "namespace.vndk_in_system.whitelisted = libz.so:libyuv.so:libtinyxml2.so\n"
   "\n";
 
 static bool write_version(const std::string& path, uint32_t version) {
@@ -165,20 +172,24 @@
   ASSERT_FALSE(default_ns_links[1].allow_all_shared_libs());
 
   auto& ns_configs = config->namespace_configs();
-  ASSERT_EQ(3U, ns_configs.size());
+  ASSERT_EQ(4U, ns_configs.size());
 
   // find second namespace
   const NamespaceConfig* ns_system = nullptr;
   const NamespaceConfig* ns_vndk = nullptr;
+  const NamespaceConfig* ns_vndk_in_system = nullptr;
   for (auto& ns : ns_configs) {
     std::string ns_name = ns->name();
-    ASSERT_TRUE(ns_name == "system" || ns_name == "default" || ns_name == "vndk")
+    ASSERT_TRUE(ns_name == "system" || ns_name == "default" ||
+                ns_name == "vndk" || ns_name == "vndk_in_system")
         << "unexpected ns name: " << ns->name();
 
     if (ns_name == "system") {
       ns_system = ns.get();
     } else if (ns_name == "vndk") {
       ns_vndk = ns.get();
+    } else if (ns_name == "vndk_in_system") {
+      ns_vndk_in_system = ns.get();
     }
   }
 
@@ -199,6 +210,11 @@
   ASSERT_EQ(1U, ns_vndk_links.size());
   ASSERT_EQ("default", ns_vndk_links[0].ns_name());
   ASSERT_TRUE(ns_vndk_links[0].allow_all_shared_libs());
+
+  ASSERT_TRUE(ns_vndk_in_system != nullptr) << "vndk_in_system namespace was not found";
+  ASSERT_EQ(
+      std::vector<std::string>({"libz.so", "libyuv.so", "libtinyxml2.so"}),
+      ns_vndk_in_system->whitelisted_libs());
 }
 
 TEST(linker_config, smoke) {
diff --git a/linker/linker_namespaces.cpp b/linker/linker_namespaces.cpp
index fd72cdc..e870ef7 100644
--- a/linker/linker_namespaces.cpp
+++ b/linker/linker_namespaces.cpp
@@ -38,6 +38,14 @@
     return true;
   }
 
+  if (!whitelisted_libs_.empty()) {
+    const char *lib_name = basename(file.c_str());
+    if (std::find(whitelisted_libs_.begin(), whitelisted_libs_.end(),
+                  lib_name) == whitelisted_libs_.end()) {
+      return false;
+    }
+  }
+
   for (const auto& dir : ld_library_paths_) {
     if (file_is_in_dir(file, dir)) {
       return true;
diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h
index cd8b09d..31aeeb6 100644
--- a/linker/linker_namespaces.h
+++ b/linker/linker_namespaces.h
@@ -110,6 +110,16 @@
     permitted_paths_ = permitted_paths;
   }
 
+  const std::vector<std::string>& get_whitelisted_libs() const {
+    return whitelisted_libs_;
+  }
+  void set_whitelisted_libs(std::vector<std::string>&& whitelisted_libs) {
+    whitelisted_libs_ = whitelisted_libs;
+  }
+  void set_whitelisted_libs(const std::vector<std::string>& whitelisted_libs) {
+    whitelisted_libs_ = whitelisted_libs;
+  }
+
   const std::vector<android_namespace_link_t>& linked_namespaces() const {
     return linked_namespaces_;
   }
@@ -157,6 +167,7 @@
   std::vector<std::string> ld_library_paths_;
   std::vector<std::string> default_library_paths_;
   std::vector<std::string> permitted_paths_;
+  std::vector<std::string> whitelisted_libs_;
   // Loader looks into linked namespace if it was not able
   // to find a library in this namespace. Note that library
   // lookup in linked namespaces are limited by the list of