Fuzz nested interfaces.

Bug: 62191146
Test: SANITIZE_TARGET="address coverage" make vts -j64 && vts-tradefed
run commandAndExit vts --skip-all-system-status-check --primary-abi-only
--skip-preconditions -l VERBOSE --module VtsHalRenderscriptV1_0IfaceFuzzer
Change-Id: I6f2ea5681a37dfb4c9cfd5a302da19a020a6a6de
diff --git a/iface_fuzzer/ProtoFuzzerRunner.cpp b/iface_fuzzer/ProtoFuzzerRunner.cpp
index bdd8345..eb8deb1 100644
--- a/iface_fuzzer/ProtoFuzzerRunner.cpp
+++ b/iface_fuzzer/ProtoFuzzerRunner.cpp
@@ -67,33 +67,33 @@
   return service_name;
 }
 
-static FuzzerBase *InitHalDriver(const CompSpec &comp_spec, bool binder_mode) {
+static void *Dlopen(string lib_name) {
   const char *error;
-  string driver_name = GetDriverName(comp_spec);
-  void *handle = dlopen(driver_name.c_str(), RTLD_LAZY);
-  if (!handle) {
-    cerr << __func__ << ": " << dlerror() << endl;
-    cerr << __func__ << ": Can't load shared library: " << driver_name << endl;
-    exit(1);
-  }
-
   // Clear dlerror().
   dlerror();
-  string function_name = GetFunctionNamePrefix(comp_spec);
-  using loader_func = FuzzerBase *(*)();
-  auto hal_loader = (loader_func)dlsym(handle, function_name.c_str());
+  void *handle = dlopen(lib_name.c_str(), RTLD_LAZY);
+  if (!handle) {
+    cerr << __func__ << ": " << dlerror() << endl;
+    cerr << __func__ << ": Can't load shared library: " << lib_name << endl;
+    exit(1);
+  }
+  return handle;
+}
+
+static void *Dlsym(void *handle, string function_name) {
+  const char *error;
+  // Clear dlerror().
+  dlerror();
+  void *function = dlsym(handle, function_name.c_str());
   if ((error = dlerror()) != NULL) {
     cerr << __func__ << ": Can't find: " << function_name << endl;
     cerr << error << endl;
     exit(1);
   }
+  return function;
+}
 
-  FuzzerBase *hal = hal_loader();
-  string service_name = GetServiceName(comp_spec);
-  cerr << "HAL name: " << comp_spec.package() << endl
-       << "Interface name: " << comp_spec.component_name() << endl
-       << "Service name: " << service_name << endl;
-
+static void GetService(FuzzerBase *hal, string service_name, bool binder_mode) {
   // For fuzzing, only passthrough mode provides coverage.
   // If binder mode is not requested, attempt to open HAL in passthrough mode.
   // If the attempt fails, fall back to binder mode.
@@ -103,7 +103,7 @@
            << "Falling back to binder mode." << endl;
     } else {
       cerr << "HAL opened in passthrough mode." << endl;
-      return hal;
+      return;
     }
   }
 
@@ -112,6 +112,30 @@
     exit(1);
   } else {
     cerr << "HAL opened in binder mode." << endl;
+    return;
+  }
+}
+
+FuzzerBase *ProtoFuzzerRunner::LoadInterface(const CompSpec &comp_spec,
+                                             uint64_t hidl_service = 0) {
+  FuzzerBase *hal;
+  const char *error;
+  // Clear dlerror().
+  dlerror();
+
+  // FuzzerBase can be constructed with or without an argument.
+  // Using different FuzzerBase constructors requires dlsym'ing different
+  // symbols from the driver library.
+  string function_name = GetFunctionNamePrefix(comp_spec);
+  if (hidl_service) {
+    function_name += "with_arg";
+    using loader_func = FuzzerBase *(*)(uint64_t);
+    auto hal_loader = (loader_func)Dlsym(driver_handle_, function_name.c_str());
+    hal = hal_loader(hidl_service);
+  } else {
+    using loader_func = FuzzerBase *(*)();
+    auto hal_loader = (loader_func)Dlsym(driver_handle_, function_name.c_str());
+    hal = hal_loader();
   }
   return hal;
 }
@@ -127,7 +151,18 @@
 
 void ProtoFuzzerRunner::Init(const string &iface_name, bool binder_mode) {
   const CompSpec *comp_spec = FindCompSpec(iface_name);
-  std::shared_ptr<FuzzerBase> hal{InitHalDriver(*comp_spec, binder_mode)};
+  // dlopen VTS driver library.
+  string driver_name = GetDriverName(*comp_spec);
+  driver_handle_ = Dlopen(driver_name);
+
+  std::shared_ptr<FuzzerBase> hal{LoadInterface(*comp_spec)};
+  string service_name = GetServiceName(*comp_spec);
+  cerr << "HAL name: " << comp_spec->package() << endl
+       << "Interface name: " << comp_spec->component_name() << endl
+       << "Service name: " << service_name << endl;
+
+  // This should only be done for top-level interfaces.
+  GetService(hal.get(), service_name, binder_mode);
 
   // Register this interface as opened by the runner.
   opened_ifaces_[iface_name] = {
@@ -136,19 +171,51 @@
 }
 
 void ProtoFuzzerRunner::Execute(const ExecSpec &exec_spec) {
-  for (const auto &iface_func_call : exec_spec.function_call()) {
-    string iface_name = iface_func_call.hidl_interface_name();
-    const FuncSpec &func_spec = iface_func_call.api();
+  for (const auto &func_call : exec_spec.function_call()) {
+    Execute(func_call);
+  }
+}
 
-    auto iface_desc = opened_ifaces_.find(iface_name);
-    if (iface_desc == opened_ifaces_.end()) {
-      cerr << "Interface is not open: " << iface_name << endl;
-      exit(1);
+void ProtoFuzzerRunner::Execute(const FuncCall &func_call) {
+  string iface_name = func_call.hidl_interface_name();
+  const FuncSpec &func_spec = func_call.api();
+
+  auto iface_desc = opened_ifaces_.find(iface_name);
+  if (iface_desc == opened_ifaces_.end()) {
+    cerr << "Interface is not open: " << iface_name << endl;
+    exit(1);
+  }
+  cout << func_call.DebugString() << endl;
+
+  FuncSpec result{};
+  iface_desc->second.hal_->CallFunction(func_spec, "", &result);
+
+  ProcessReturnValue(result);
+}
+
+static string StripNamespace(const string &type) {
+  size_t idx = type.find_last_of(':');
+  if (idx == string::npos) {
+    return "";
+  }
+  return type.substr(idx + 1);
+}
+
+void ProtoFuzzerRunner::ProcessReturnValue(const FuncSpec &result) {
+  for (const auto &var : result.return_type_hidl()) {
+    if (var.has_hidl_interface_pointer() && var.has_predefined_type()) {
+      uint64_t hidl_service = var.hidl_interface_pointer();
+      string type = var.predefined_type();
+      string iface_name = StripNamespace(type);
+
+      const CompSpec *comp_spec = FindCompSpec(iface_name);
+      std::shared_ptr<FuzzerBase> hal{LoadInterface(*comp_spec, hidl_service)};
+
+      // Register this interface as opened by the runner.
+      opened_ifaces_[iface_name] = {
+          .comp_spec_ = comp_spec, .hal_ = hal,
+      };
     }
-    cout << iface_func_call.DebugString() << endl;
-
-    FuncSpec result{};
-    iface_desc->second.hal_->CallFunction(func_spec, "", &result);
   }
 }
 
diff --git a/iface_fuzzer/include/ProtoFuzzerRunner.h b/iface_fuzzer/include/ProtoFuzzerRunner.h
index 06af6b9..124b2b9 100644
--- a/iface_fuzzer/include/ProtoFuzzerRunner.h
+++ b/iface_fuzzer/include/ProtoFuzzerRunner.h
@@ -45,6 +45,8 @@
   void Init(const std::string &, bool);
   // Call every API from call sequence specified by the ExecSpec.
   void Execute(const ExecSpec &);
+  // Execute the specified interface function call.
+  void Execute(const FuncCall &);
   // Accessor to interface descriptor table containing currently opened
   // interfaces.
   const IfaceDescTbl &GetOpenedIfaces() const { return opened_ifaces_; }
@@ -52,11 +54,18 @@
  private:
   // Looks up interface spec by name.
   const CompSpec *FindCompSpec(std::string);
+  // Processes return value from a function call.
+  void ProcessReturnValue(const FuncSpec &result);
+  // Loads the interface corresponding to the given VTS spec. Interface is
+  // constructed with the given argument.
+  FuzzerBase *LoadInterface(const CompSpec &, uint64_t);
 
   // Keeps track of opened interfaces.
   IfaceDescTbl opened_ifaces_;
   // All loaded VTS specs indexed by name.
   std::unordered_map<std::string, CompSpec> comp_specs_;
+  // Handle to the driver library.
+  void *driver_handle_;
 };
 
 }  // namespace fuzzer