Added updateEngine_downloadAction_fuzzer

Test: ./updateEngine_downloadAction_fuzzer
Bug: 193202513

Change-Id: If736e8a2bec73b5af18264ccdd2c3f56ded8c3dd
diff --git a/fuzzer/Android.bp b/fuzzer/Android.bp
new file mode 100644
index 0000000..04cf9db
--- /dev/null
+++ b/fuzzer/Android.bp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+cc_defaults {
+    name: "updateEngineFuzzer_defaults",
+    static_libs: [
+        "libupdate_engine_android",
+        "libavb",
+        "libavb_user",
+        "gkiprops",
+        "libpayload_consumer",
+        "libupdate_engine_boot_control",
+        "PlatformProperties",
+    ],
+    shared_libs: [
+        "apex_aidl_interface-cpp",
+        "libandroid_net",
+        "libbase",
+        "libbinder",
+        "libbinderwrapper",
+        "libbootloader_message",
+        "libbrillo-binder",
+        "libcurl",
+        "libcutils",
+        "libupdate_engine_stable-V1-cpp",
+        "liblog",
+        "libssl",
+        "libstatslog",
+        "libutils",
+        "libbrillo-stream",
+        "libbrillo",
+        "libchrome",
+    ],
+    include_dirs: ["system"],
+    fuzz_config: {
+        cc: [
+            "android-ota@google.com",
+        ],
+        componentid: 155276,
+    },
+}
+
+cc_fuzz {
+    name: "updateEngine_downloadAction_fuzzer",
+    srcs: [
+        "updateEngine_downloadAction_fuzzer.cpp",
+    ],
+    defaults: [
+        "updateEngineFuzzer_defaults",
+        "libupdate_engine_boot_control_exports",
+        "libpayload_consumer_exports",
+    ],
+    cflags: [
+        "-Wno-unused-parameter",
+    ],
+}
diff --git a/fuzzer/README.md b/fuzzer/README.md
new file mode 100644
index 0000000..a54dd31
--- /dev/null
+++ b/fuzzer/README.md
@@ -0,0 +1,57 @@
+# Fuzzer for libupdate_engine_android
+## Table of contents
++ [updateEngine_downloadAction_fuzzer](#updateEngine_downloadAction_fuzzer)
+
+# <a name="updateEngine_downloadAction_fuzzer"></a> Fuzzer for download_action
+
+## Plugin Design Considerations
+The fuzzer plugin for libupdate_engine_android is designed based on the understanding of the library
+ and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libupdate_engine_android supports the following parameters:
+1. Version (parameter name: `version`)
+2. Already_Applied (parameter name: `already_applied`)
+3. Is_Resume (parameter name: `is_resume`)
+4. Interactive (parameter name: `interactive`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `version` | `String` | Value obtained from FuzzedDataProvider|
+| `already_applied` | `true` or `false` | Value obtained from FuzzedDataProvider|
+| `is_resume` | `true` or `false` | Value obtained from FuzzedDataProvider|
+| `interactive` | `true` or `false` | Value obtained from FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the libupdate_engine_android module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build updateEngine_downloadAction_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) updateEngine_downloadAction_fuzzer
+```
+#### Steps to run
+
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/${TARGET_ARCH}/updateEngine_downloadAction_fuzzer/updateEngine_downloadAction_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/fuzzer/updateEngine_downloadAction_fuzzer.cpp b/fuzzer/updateEngine_downloadAction_fuzzer.cpp
new file mode 100644
index 0000000..e877273
--- /dev/null
+++ b/fuzzer/updateEngine_downloadAction_fuzzer.cpp
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <update_engine/common/download_action.h>
+#include <update_engine/common/boot_control_stub.h>
+#include <update_engine/common/hardware_interface.h>
+#include <update_engine/common/http_fetcher.h>
+#include <update_engine/common/error_code.h>
+#include <update_engine/common/proxy_resolver.h>
+#include <update_engine/common/action_processor.h>
+#include <string.h>
+#include <utils/Log.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using namespace chromeos_update_engine;
+using namespace std;
+
+constexpr size_t kSizeMin = 1;
+constexpr size_t kSizeMax = 1000;
+constexpr size_t kHashSize = 32;
+constexpr size_t kStringMaxLength = 20;
+const string kDownloadUrl = "http://fake_url.invalid";
+const string kSourcePath = "/dev/zero";
+const string kTargetPath = "/dev/null";
+
+class TestPrefsInterface : public PrefsInterface {
+ public:
+  TestPrefsInterface(FuzzedDataProvider* fdp) : mFdp(fdp) {
+    mMetadataSize = mFdp->ConsumeIntegralInRange<int64_t>(kSizeMin, kSizeMax);
+    mSignatureSize = mFdp->ConsumeIntegralInRange<int64_t>(kSizeMin, kSizeMax);
+  };
+
+  bool GetString(std::string_view key, std::string* value) const {
+    if (key == "manifest-bytes") {
+      *value = mFdp->ConsumeRandomLengthString(mMetadataSize + mSignatureSize);
+      return true;
+    }
+    *value = "";
+    return false;
+  };
+
+  bool SetString(std::string_view /*key*/, std::string_view /*value*/) {
+    return true;
+  };
+
+  bool GetInt64(std::string_view key, int64_t* value) const {
+    if (key == "manifest-metadata-size") {
+      *value = mMetadataSize;
+      return true;
+    }
+    if (key == "manifest-signature-size") {
+      *value = mSignatureSize;
+      return true;
+    }
+    *value = 0;
+    return false;
+  }
+
+  bool SetInt64(std::string_view /*key*/, const int64_t /*value*/) {
+    return true;
+  }
+
+  bool GetBoolean(std::string_view /*key*/, bool* value) const {
+    *value = true;
+    return true;
+  }
+
+  bool SetBoolean(std::string_view /*key*/, const bool /*value*/) {
+    return true;
+  }
+
+  bool Exists(std::string_view /*key*/) const { return true; }
+
+  bool Delete(std::string_view /*key*/) { return true; }
+
+  bool Delete(std::string_view /*pref_key*/,
+              const std::vector<std::string>& /*nss*/) {
+    return true;
+  }
+
+  bool GetSubKeys(std::string_view /*ns*/,
+                  std::vector<std::string>* keys) const {
+    keys->push_back("");
+    return true;
+  }
+
+  void AddObserver(std::string_view /*key*/, ObserverInterface* /*observer*/) {}
+
+  void RemoveObserver(std::string_view /*key*/,
+                      ObserverInterface* /*observer*/) {}
+
+ private:
+  FuzzedDataProvider* mFdp;
+  int64_t mMetadataSize;
+  int64_t mSignatureSize;
+};
+
+class TestHardwareInterface : public HardwareInterface {
+ public:
+  bool IsOfficialBuild() const { return true; };
+
+  bool IsNormalBootMode() const { return true; };
+
+  bool AreDevFeaturesEnabled() const { return true; };
+
+  bool IsOOBEEnabled() const { return true; };
+
+  bool IsOOBEComplete(base::Time* /*out_time_of_oobe*/) const { return true; };
+
+  string GetHardwareClass() const { return ""; };
+
+  string GetDeviceRequisition() const { return ""; };
+
+  int32_t GetMinKernelKeyVersion() const { return 0; };
+
+  int32_t GetMinFirmwareKeyVersion() const { return 0; };
+
+  int32_t GetMaxFirmwareKeyRollforward() const { return 0; };
+
+  bool SetMaxFirmwareKeyRollforward(int32_t /*firmware_max_rollforward*/) {
+    return true;
+  };
+
+  bool SetMaxKernelKeyRollforward(int32_t /*kernel_max_rollforward*/) {
+    return true;
+  };
+
+  int32_t GetPowerwashCount() const { return 0; };
+
+  bool SchedulePowerwash(bool /*save_rollback_data*/) { return true; };
+
+  bool CancelPowerwash() { return true; };
+
+  bool GetNonVolatileDirectory(base::FilePath* path) const {
+    base::FilePath local_path(constants::kNonVolatileDirectory);
+    *path = local_path;
+    return true;
+  };
+
+  bool GetPowerwashSafeDirectory(base::FilePath* /*path*/) const {
+    return false;
+  };
+
+  int64_t GetBuildTimestamp() const { return 0; };
+
+  bool AllowDowngrade() const { return true; };
+
+  bool GetFirstActiveOmahaPingSent() const { return true; };
+
+  bool SetFirstActiveOmahaPingSent() { return true; };
+
+  void SetWarmReset(bool /*warm_reset*/){};
+
+  void SetVbmetaDigestForInactiveSlot(bool /*reset*/){};
+
+  string GetVersionForLogging(const string& /*partition_name*/) const {
+    return "";
+  };
+
+  ErrorCode IsPartitionUpdateValid(const string& /*partition_name*/,
+                                   const string& /*new_version*/) const {
+    return ErrorCode::kSuccess;
+  };
+
+  const char* GetPartitionMountOptions(const string& /*partition_name*/) const {
+    return "";
+  };
+};
+
+class TestProxyResolver : public ProxyResolver {
+ public:
+  virtual ~TestProxyResolver() {}
+
+  ProxyRequestId GetProxiesForUrl(const string& /*url*/,
+                                  const ProxiesResolvedFn& /*callback*/) {
+    return 0;
+  };
+
+  bool CancelProxyRequest(ProxyRequestId /*request*/) { return true; };
+};
+
+class TestHttpFetcher : public HttpFetcher {
+ public:
+  TestHttpFetcher(ProxyResolver* proxy_resolver, FuzzedDataProvider* fdp)
+      : HttpFetcher(proxy_resolver), mFdp(fdp){};
+  virtual ~TestHttpFetcher() {}
+
+  void SetOffset(off_t /*offset*/) {}
+
+  void SetLength(size_t /*length*/) {}
+  void UnsetLength() {}
+
+  void BeginTransfer(const string& /*url*/) {
+    if (mFdp->remaining_bytes() > 0) {
+      size_t maxSize = mFdp->ConsumeIntegralInRange<size_t>(kSizeMin, kSizeMax);
+      vector<uint8_t> data = mFdp->ConsumeBytes<uint8_t>(maxSize);
+      delegate()->ReceivedBytes(this, data.data(), data.size());
+    }
+  }
+
+  void TerminateTransfer() {}
+
+  void SetHeader(const string& /*header_name*/,
+                 const string& /*header_value*/) {}
+
+  bool GetHeader(const string& /*header_name*/, string* header_value) const {
+    *header_value = "";
+    return true;
+  }
+
+  void Pause() {}
+
+  void Unpause() {}
+
+  void set_idle_seconds(int32_t /*seconds*/) {}
+  void set_retry_seconds(int32_t /*seconds*/) {}
+
+  void set_low_speed_limit(int32_t /*low_speed_bps*/,
+                           int32_t /*low_speed_sec*/) {}
+
+  void set_connect_timeout(int32_t /*connect_timeout_seconds*/) {}
+
+  void set_max_retry_count(int32_t /*max_retry_count*/) {}
+
+  size_t GetBytesDownloaded() { return 0; }
+
+ private:
+  FuzzedDataProvider* mFdp;
+};
+
+class TestActionProcessor : public ActionProcessor {
+ public:
+  void StartProcessing() {}
+
+  void EnqueueAction(unique_ptr<AbstractAction> /*action*/){};
+
+  void ActionComplete(AbstractAction* /*actionptr*/, ErrorCode /*code*/){};
+};
+
+class chromeos_update_engine::DownloadActionTest {
+ public:
+  DownloadActionTest() : mActionPipe(new ActionPipe<InstallPlan>()) {}
+  ~DownloadActionTest() { mActionPipe = nullptr; }
+
+  void fuzzDownloadAction(const uint8_t* data, size_t size);
+
+ private:
+  shared_ptr<ActionPipe<InstallPlan>> mActionPipe = nullptr;
+};
+
+void DownloadActionTest::fuzzDownloadAction(const uint8_t* data, size_t size) {
+  FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+
+  InstallPlan install_plan;
+  install_plan.download_url = kDownloadUrl;
+  install_plan.version = fdp.ConsumeRandomLengthString(kStringMaxLength);
+  install_plan.target_slot = fdp.ConsumeBool() ? 0 : 1;
+  install_plan.hash_checks_mandatory = fdp.ConsumeBool();
+  InstallPlan::Partition partition;
+  partition.name = fdp.ConsumeRandomLengthString(kStringMaxLength);
+  partition.source_path = kSourcePath;
+  partition.source_size = fdp.ConsumeIntegralInRange(kSizeMin, kSizeMax);
+  partition.target_path = kTargetPath;
+  partition.target_size = fdp.ConsumeIntegralInRange(kSizeMin, kSizeMax);
+  install_plan.partitions.push_back(partition);
+  InstallPlan::Payload payload;
+  payload.payload_urls.emplace_back(kDownloadUrl);
+  payload.size = fdp.ConsumeIntegralInRange(kSizeMin, kSizeMax);
+  payload.metadata_size =
+      fdp.ConsumeIntegralInRange<uint64_t>(kSizeMin, kSizeMax),
+  payload.hash = fdp.ConsumeBytes<uint8_t>(kHashSize),
+  payload.already_applied = fdp.ConsumeBool();
+  install_plan.payloads.push_back(payload);
+  install_plan.is_resume = fdp.ConsumeBool();
+  mActionPipe->set_contents(install_plan);
+
+  TestPrefsInterface prefs(&fdp);
+  BootControlStub boot_control;
+  TestHardwareInterface hardwareInterface;
+  TestProxyResolver proxyResolver;
+  TestHttpFetcher* httpFetcher = new TestHttpFetcher(&proxyResolver, &fdp);
+  bool interactive = fdp.ConsumeBool();
+  unique_ptr<DownloadAction> downloadAction = make_unique<DownloadAction>(
+      &prefs, &boot_control, &hardwareInterface, httpFetcher, interactive);
+
+  downloadAction->set_in_pipe(mActionPipe);
+  TestActionProcessor actionProcessor;
+  downloadAction->SetProcessor(&actionProcessor);
+
+  downloadAction->PerformAction();
+}
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  DownloadActionTest downloadActionFuzzer;
+  downloadActionFuzzer.fuzzDownloadAction(data, size);
+  return 0;
+}