diff options
author | Aditya Wazir <aditya.wazir@ittiam.com> | 2022-04-05 01:28:43 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-04-05 01:28:43 +0000 |
commit | b0bda4937f4ce51a3014ec6c5e72d23220d927f8 (patch) | |
tree | cc0a00cbb0a4cb515702f9ebf4467d4ad6e1244e | |
parent | 9ffb6c0508ad916c60b861dbdb1644ffdb3ec396 (diff) | |
parent | 55fec45931f4830ff047b42ef678e0178cca28fc (diff) | |
download | update_engine-b0bda4937f4ce51a3014ec6c5e72d23220d927f8.tar.gz |
Added updateEngine_downloadAction_fuzzer am: 55fec45931
Original change: https://android-review.googlesource.com/c/platform/system/update_engine/+/1810483
Change-Id: Ie91c0da6e4739cfb8bdef63378590974654ab8f6
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r-- | fuzzer/Android.bp | 68 | ||||
-rw-r--r-- | fuzzer/README.md | 57 | ||||
-rw-r--r-- | fuzzer/updateEngine_downloadAction_fuzzer.cpp | 309 |
3 files changed, 434 insertions, 0 deletions
diff --git a/fuzzer/Android.bp b/fuzzer/Android.bp new file mode 100644 index 00000000..04cf9db8 --- /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 00000000..a54dd31a --- /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 00000000..e8772733 --- /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; +} |