From 5eb2d6fa27f5e92032ad61c9b674af5f7344f19b Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Thu, 30 Apr 2020 14:21:27 -0700 Subject: [PATCH 1/8] libsnapshot_fuzzer: map super image ... instead of operating on the image file directly. Test: run it Bug: 154633114 Change-Id: Id04c0d15d0d52483647716f8bfb0b8ee1a2876d9 --- fs_mgr/libsnapshot/snapshot_fuzz.cpp | 4 +- fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp | 126 ++++++++++++++------- fs_mgr/libsnapshot/snapshot_fuzz_utils.h | 28 +++-- 3 files changed, 103 insertions(+), 55 deletions(-) diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp index 12a0531c3..3671bb052 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp +++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp @@ -125,14 +125,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { static FuzzSnapshotManager fuzz_snapshot_manager; [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit(); - CHECK(env.InitOk()); + env.CheckSoftReset(); FuzzData fuzz_data(data, size); auto snapshot_manager_data = fuzz_data.Consume(); if (!snapshot_manager_data.has_value()) { return 0; } - auto snapshot_manager = env.CreateSnapshotManager(snapshot_manager_data.value()); + auto snapshot_manager = env.CheckCreateSnapshotManager(snapshot_manager_data.value()); CHECK(snapshot_manager); fuzz_snapshot_manager.DepleteData(snapshot_manager.get(), &fuzz_data); diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp index d5e3e968e..7829193f5 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp +++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -29,17 +30,27 @@ #include #include "snapshot_fuzz_utils.h" +#include "utility.h" + +// Prepends the errno string, but it is good enough. +#ifndef PCHECK +#define PCHECK(x) CHECK(x) << strerror(errno) << ": " +#endif using namespace android::storage_literals; +using namespace std::chrono_literals; using namespace std::string_literals; using android::base::StringPrintf; using android::base::unique_fd; using android::base::WriteStringToFile; +using android::dm::LoopControl; using android::fiemap::IImageManager; using android::fiemap::ImageManager; -static const char MNT_DIR[] = "/mnt"; +// This directory is exempted from pinning in ImageManager. +static const char MNT_DIR[] = "/data/gsi/ota/test/"; + static const char FAKE_ROOT_NAME[] = "snapshot_fuzz"; static const auto SUPER_IMAGE_SIZE = 16_MiB; static const auto FAKE_ROOT_SIZE = 64_MiB; @@ -128,6 +139,9 @@ class AutoUnmount : public AutoDevice { class AutoMemBasedDir : public AutoDevice { public: static std::unique_ptr New(const std::string& name, uint64_t size) { + if (!Mkdir(MNT_DIR)) { + return std::unique_ptr(new AutoMemBasedDir("")); + } auto ret = std::unique_ptr(new AutoMemBasedDir(name)); ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path()); if (!ret->auto_delete_mount_dir_->HasDevice()) { @@ -137,20 +151,31 @@ class AutoMemBasedDir : public AutoDevice { if (!ret->auto_umount_mount_point_->HasDevice()) { return std::unique_ptr(new AutoMemBasedDir("")); } - // path() does not need to be deleted upon destruction, hence it is not wrapped with - // AutoDeleteDir. - if (!Mkdir(ret->path())) { + // tmp_path() and persist_path does not need to be deleted upon destruction, hence it is + // not wrapped with AutoDeleteDir. + if (!Mkdir(ret->tmp_path())) { + return std::unique_ptr(new AutoMemBasedDir("")); + } + if (!Mkdir(ret->persist_path())) { return std::unique_ptr(new AutoMemBasedDir("")); } return ret; } - // Return the scratch directory. - std::string path() const { + // Return the temporary scratch directory. + std::string tmp_path() const { CHECK(HasDevice()); - return mount_path() + "/root"; + return mount_path() + "/tmp"; + } + // Return the temporary scratch directory. + std::string persist_path() const { + CHECK(HasDevice()); + return mount_path() + "/persist"; + } + // Delete all contents in tmp_path() and start over. tmp_path() itself is re-created. + void CheckSoftReset() { + PCHECK(RmdirRecursive(tmp_path())); + PCHECK(Mkdir(tmp_path())); } - // Delete all contents in path() and start over. path() itself is re-created. - bool SoftReset() { return RmdirRecursive(path()) && Mkdir(path()); } private: AutoMemBasedDir(const std::string& name) : AutoDevice(name) {} @@ -164,64 +189,83 @@ class AutoMemBasedDir : public AutoDevice { SnapshotFuzzEnv::SnapshotFuzzEnv() { fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE); + CHECK(fake_root_ != nullptr); + CHECK(fake_root_->HasDevice()); + loop_control_ = std::make_unique(); + mapped_super_ = CheckMapSuper(fake_root_->persist_path(), loop_control_.get(), &fake_super_); } SnapshotFuzzEnv::~SnapshotFuzzEnv() = default; -bool SnapshotFuzzEnv::InitOk() const { - if (fake_root_ == nullptr || !fake_root_->HasDevice()) return false; - return true; +void CheckZeroFill(const std::string& file, size_t size) { + std::string zeros(size, '\0'); + PCHECK(WriteStringToFile(zeros, file)) << "Cannot write zeros to " << file; } -bool SnapshotFuzzEnv::SoftReset() { - return fake_root_->SoftReset(); +void SnapshotFuzzEnv::CheckSoftReset() { + fake_root_->CheckSoftReset(); + CheckZeroFill(super(), SUPER_IMAGE_SIZE); } -std::unique_ptr SnapshotFuzzEnv::CreateFakeImageManager( - const std::string& fake_root) { - auto images_dir = fake_root + "/images"; +std::unique_ptr SnapshotFuzzEnv::CheckCreateFakeImageManager( + const std::string& path) { + auto images_dir = path + "/images"; auto metadata_dir = images_dir + "/metadata"; auto data_dir = images_dir + "/data"; - if (!Mkdir(images_dir) || !Mkdir(metadata_dir) || !Mkdir(data_dir)) { - return nullptr; - } + PCHECK(Mkdir(images_dir)); + PCHECK(Mkdir(metadata_dir)); + PCHECK(Mkdir(data_dir)); return ImageManager::Open(metadata_dir, data_dir); } -std::unique_ptr SnapshotFuzzEnv::CreatePartitionOpener( - const std::string& fake_root) { - auto fake_super = fake_root + "/super.img"; - std::string zeros(SUPER_IMAGE_SIZE, '\0'); - - if (!WriteStringToFile(zeros, fake_super)) { - PLOG(ERROR) << "Cannot write zeros to " << fake_super; - return nullptr; - } - - return std::make_unique(fake_super); +// Helper to create a loop device for a file. +static void CheckCreateLoopDevice(LoopControl* control, const std::string& file, + const std::chrono::milliseconds& timeout_ms, std::string* path) { + static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC; + android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags)); + PCHECK(file_fd >= 0) << "Could not open file: " << file; + CHECK(control->Attach(file_fd, timeout_ms, path)) + << "Could not create loop device for: " << file; } -std::string SnapshotFuzzEnv::root() const { - CHECK(InitOk()); - return fake_root_->path(); +class AutoDetachLoopDevice : public AutoDevice { + public: + AutoDetachLoopDevice(LoopControl* control, const std::string& device) + : AutoDevice(device), control_(control) {} + ~AutoDetachLoopDevice() { control_->Detach(name_); } + + private: + LoopControl* control_; +}; + +std::unique_ptr SnapshotFuzzEnv::CheckMapSuper(const std::string& fake_persist_path, + LoopControl* control, + std::string* fake_super) { + auto super_img = fake_persist_path + "/super.img"; + CheckZeroFill(super_img, SUPER_IMAGE_SIZE); + CheckCreateLoopDevice(control, super_img, 1s, fake_super); + + return std::make_unique(control, *fake_super); } -std::unique_ptr SnapshotFuzzEnv::CreateSnapshotManager( +std::unique_ptr SnapshotFuzzEnv::CheckCreateSnapshotManager( const SnapshotManagerFuzzData& data) { - // TODO(b/154633114): create valid super partition according to fuzz data - auto partition_opener = CreatePartitionOpener(root()); - if (partition_opener == nullptr) return nullptr; - auto metadata_dir = root() + "/snapshot_metadata"; - if (!Mkdir(metadata_dir)) return nullptr; + auto partition_opener = std::make_unique(super()); + auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata"; + PCHECK(Mkdir(metadata_dir)); auto device_info = new SnapshotFuzzDeviceInfo(data.device_info_data, std::move(partition_opener), metadata_dir); auto snapshot = SnapshotManager::New(device_info /* takes ownership */); - snapshot->images_ = CreateFakeImageManager(root()); + snapshot->images_ = CheckCreateFakeImageManager(fake_root_->tmp_path()); snapshot->has_local_image_manager_ = data.is_local_image_manager; return snapshot; } +const std::string& SnapshotFuzzEnv::super() const { + return fake_super_; +} + } // namespace android::snapshot diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h index 32910a9ad..8c0d5dd54 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h +++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -52,27 +53,30 @@ class SnapshotFuzzEnv { SnapshotFuzzEnv(); ~SnapshotFuzzEnv(); - // Check if environment is initialized properly. - bool InitOk() const; - - // A scratch directory for the test to play around with. The scratch directory - // is backed by tmpfs. SoftReset() clears the directory. - std::string root() const; - // Soft reset part of the environment before running the next test. - bool SoftReset(); + // Abort if fails. + void CheckSoftReset(); // Create a snapshot manager for this test run. // Client is responsible for maintaining the lifetime of |data| over the life time of // ISnapshotManager. - std::unique_ptr CreateSnapshotManager(const SnapshotManagerFuzzData& data); + std::unique_ptr CheckCreateSnapshotManager( + const SnapshotManagerFuzzData& data); + + // Return path to super partition. + const std::string& super() const; private: std::unique_ptr fake_root_; + std::unique_ptr loop_control_; + std::unique_ptr mapped_super_; + std::string fake_super_; - static std::unique_ptr CreateFakeImageManager( - const std::string& fake_root); - static std::unique_ptr CreatePartitionOpener(const std::string& fake_root); + static std::unique_ptr CheckCreateFakeImageManager( + const std::string& fake_tmp_path); + static std::unique_ptr CheckMapSuper(const std::string& fake_persist_path, + android::dm::LoopControl* control, + std::string* fake_super); }; class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo { From 90a9393ea04ca4e40b07d94ddc6336ba6228db5e Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Tue, 28 Apr 2020 23:39:05 -0700 Subject: [PATCH 2/8] libsnapshot_fuzzer: use protobuf Use protobuf because it already has all the fuzzing implemenetations. Delete fuzz_utils. Pros: - Fuzzing protobuf is faster; it is easy to achieve 4K exec/s - It is more guided; protobufs are fuzzed using mutators, and mutators should have better knowledge of the structure of the fuzz data - No more hand-written parsing code of the fuzz data. That code in fuzz_utils.h is deleted. - Corpus data can be reused even after adding new fields in the protobuf - Corpus data is human-readable and easily manually written (it is the text format of the protobuf) Cons: - The "actions" are "declared" in protobuf definition and "defined" in C++, so there's more boilerplate to write. Adding a new "Action" requires changes in both. Test: run libsnapshot_fuzzer Bug: 154633114 Change-Id: Idc2a6b2c087e370e4cfef53142a244b9b275389e --- fs_mgr/libsnapshot/Android.bp | 10 +- .../android/snapshot/snapshot_fuzz.proto | 76 +++ fs_mgr/libsnapshot/fuzz.sh | 4 +- fs_mgr/libsnapshot/fuzz_utils.cpp | 13 + fs_mgr/libsnapshot/fuzz_utils.h | 452 +++++++++--------- fs_mgr/libsnapshot/snapshot_fuzz.cpp | 105 ++-- fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp | 6 +- fs_mgr/libsnapshot/snapshot_fuzz_utils.h | 44 +- 8 files changed, 387 insertions(+), 323 deletions(-) create mode 100644 fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index 40da1bc6d..c1213f67f 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -254,9 +254,11 @@ cc_fuzz { native_coverage : true, srcs: [ + // Compile the protobuf definition again with type full. + "android/snapshot/snapshot_fuzz.proto", + "fuzz_utils.cpp", "snapshot_fuzz.cpp", "snapshot_fuzz_utils.cpp", - "fuzz_utils.cpp", ], static_libs: [ "libbase", @@ -269,12 +271,16 @@ cc_fuzz { "liblp", "libsnapshot_init", // don't use binder or hwbinder "libsnapshot_test_helpers", - "libprotobuf-cpp-lite", + "libprotobuf-mutator", "update_metadata-protos", ], header_libs: [ "libstorage_literals_headers", ], + proto: { + type: "full", + canonical_path_from_root: false, + }, fuzz_config: { cc: ["android-virtual-ab+bugs@google.com"], diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto new file mode 100644 index 000000000..679213c47 --- /dev/null +++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto @@ -0,0 +1,76 @@ +// Copyright (C) 2020 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. + +syntax = "proto3"; +package android.snapshot; + +// Controls the behavior of IDeviceInfo. +// Next: 6 +message FuzzDeviceInfoData { + bool slot_suffix_is_a = 1; + bool is_overlayfs_setup = 2; + bool allow_set_boot_control_merge_status = 3; + bool allow_set_slot_as_unbootable = 4; + bool is_recovery = 5; +} + +// Controls the behavior of the test SnapshotManager. +// Next: 2 +message FuzzSnapshotManagerData { + bool is_local_image_manager = 1; +} + +// Mimics the API of ISnapshotManager. Defines one action on the snapshot +// manager. +// Next: 18 +message SnapshotManagerActionProto { + message NoArgs {} + message ProcessUpdateStateArgs { + bool has_before_cancel = 1; + bool fail_before_cancel = 2; + } + reserved 7; + reserved "create_update_snapshots"; + reserved 8; + reserved "map_update_snapshot"; + reserved 9; + reserved "unmap_update_snapshot"; + reserved 11; + reserved "create_logical_and_snapshot_partitions"; + reserved 14; + reserved "recovery_create_snapshot_devices_with_metadata"; + oneof value { + NoArgs begin_update = 1; + NoArgs cancel_update = 2; + bool finished_snapshot_writes = 3; + NoArgs initiate_merge = 4; + ProcessUpdateStateArgs process_update_state = 5; + bool get_update_state = 6; + NoArgs need_snapshots_in_first_stage_mount = 10; + bool handle_imminent_data_wipe = 12; + NoArgs recovery_create_snapshot_devices = 13; + NoArgs dump = 15; + NoArgs ensure_metadata_mounted = 16; + NoArgs get_snapshot_merge_stats_instance = 17; + } +} + +// Includes all data that needs to be fuzzed. +message SnapshotFuzzData { + FuzzDeviceInfoData device_info_data = 1; + FuzzSnapshotManagerData manager_data = 2; + // More data used to prep the test before running actions. + reserved 3 to 9999; + repeated SnapshotManagerActionProto actions = 10000; +} diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh index 64d822435..29101298e 100755 --- a/fs_mgr/libsnapshot/fuzz.sh +++ b/fs_mgr/libsnapshot/fuzz.sh @@ -18,8 +18,8 @@ build_normal() ( build_cov() { pushd $(gettop) - ret=$? NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET} + ret=$? popd return ${ret} } @@ -46,7 +46,7 @@ prepare_host() { } # run_snapshot_fuzz -runs=10000 -generate_corpse() { +generate_corpus() { [[ "$@" ]] || { echo "run with -runs=X"; return 1; } prepare_device && diff --git a/fs_mgr/libsnapshot/fuzz_utils.cpp b/fs_mgr/libsnapshot/fuzz_utils.cpp index 509eb1bb3..0263f7e46 100644 --- a/fs_mgr/libsnapshot/fuzz_utils.cpp +++ b/fs_mgr/libsnapshot/fuzz_utils.cpp @@ -22,4 +22,17 @@ void CheckInternal(bool value, std::string_view msg) { CHECK(value) << msg; } +const google::protobuf::OneofDescriptor* GetProtoValueDescriptor( + const google::protobuf::Descriptor* action_desc) { + CHECK(action_desc); + CHECK(action_desc->oneof_decl_count() == 1) + << action_desc->oneof_decl_count() << " oneof fields found in " << action_desc->name() + << "; only one is expected."; + auto* oneof_value_desc = action_desc->oneof_decl(0); + CHECK(oneof_value_desc); + CHECK(oneof_value_desc->name() == "value") + << "oneof field has name " << oneof_value_desc->name(); + return oneof_value_desc; +} + } // namespace android::fuzz diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h index 4e14b9c57..8504d7bd1 100644 --- a/fs_mgr/libsnapshot/fuzz_utils.h +++ b/fs_mgr/libsnapshot/fuzz_utils.h @@ -12,256 +12,242 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#pragma once + +#include #include #include -#include -// Generic classes for fuzzing a collection of APIs. +#include +#include +#include + +// Utilities for using a protobuf definition to fuzz APIs in a class. +// Terms: +// The "fuzzed class" is the C++ class definition whose functions are fuzzed. +// The "fuzzed object" is an instantiated object of the fuzzed class. It is +// typically created and destroyed for each test run. +// An "action" is an operation on the fuzzed object that may mutate its state. +// This typically involves one function call into the fuzzed object. namespace android::fuzz { -// My custom boolean type -- to avoid conflict with (u)int8_t and char. -struct Bool { - bool value; - operator bool() const { return value; } -}; - -// Helper for FuzzData. -// A wrapper over an optional const object T. The buffer is maintained elsewhere. -template -class Optional { - public: - Optional(const T* ptr) : ptr_(ptr) {} - const T& operator*() const { return *ptr_; } - const T& value() const { return *ptr_; } - bool has_value() const { return ptr_; } - - private: - const T* ptr_; -}; - -// Helper for FuzzData. -// A wrapper over an optional boolean. The boolean is owned by this object. -template <> -class Optional { - public: - Optional(std::optional&& val) : val_(std::move(val)) {} - const Bool& operator*() const { return *val_; } - const Bool& value() const { return val_.value(); } - bool has_value() const { return val_.has_value(); } - - private: - std::optional val_; -}; - -// Helper for FuzzData. -// A view on a raw data buffer. Client is responsible for maintaining the lifetime of the data -// buffer. -class DataView { - public: - DataView(const uint8_t* data, uint64_t size) : data_(data), size_(size) {} - DataView(const void* data, uint64_t size) : DataView(static_cast(data), size) {} - inline uint64_t size() const { return size_; } - inline const uint8_t* data() const { return data_; } - inline bool CanConsume(uint64_t size) { return size_ >= size; } - // Consume the first |size| bytes from |this| and return a DataView object that represents - // the consumed data. Data pointer in |this| is incremented by |size| bytes. - // If not enough bytes, return nullopt. - std::optional Consume(uint64_t size) { - if (!CanConsume(size)) return std::nullopt; - DataView ret(data_, size); - size_ -= size; - data_ += size; - return ret; - } - - private: - const uint8_t* data_; - uint64_t size_; -}; - -// A view on the fuzz data. Provides APIs to consume typed objects. -class FuzzData : public DataView { - public: - // Inherit constructors. - using DataView::DataView; - // Consume a data object T and return the pointer (into the buffer). No copy is done. - // If not enough bytes, return nullptr. - template - inline Optional Consume() { - auto data_view = DataView::Consume(sizeof(T)); - if (!data_view.has_value()) return nullptr; - return reinterpret_cast(data_view->data()); - } - // To provide enough entropy for booleans, they are consumed bit by bit. - // Hence, the returned value is not indexed into the buffer. See Optional. - template <> - Optional Consume() { - if (!boolean_buffer_.has_value() || boolean_bit_offset_ >= sizeof(*boolean_buffer_)) { - boolean_buffer_ = Consume(); - boolean_bit_offset_ = 0; - } - if (!boolean_buffer_.has_value()) { - return Optional(std::nullopt); - } - const auto& byte = *boolean_buffer_; - bool ret = (byte >> boolean_bit_offset_) & 0x1; - boolean_bit_offset_++; - return Optional(Bool{ret}); - } - - private: - // Separate buffer for booleans. - Optional boolean_buffer_ = nullptr; - uint8_t boolean_bit_offset_ = 0; -}; - -enum class CallResult { - SUCCESS, - NOT_ENOUGH_DATA, -}; - -inline bool AllArgsHasValue() { - return true; -} -template -inline bool AllArgsHasValue(const Optional& t) { - return t.has_value(); -} -template -inline bool AllArgsHasValue(const Optional& first, const Optional&... remaining) { - return first.has_value() && AllArgsHasValue(remaining...); -} - -// Base class of FuzzFunction. -class FuzzFunctionBase { - public: - virtual ~FuzzFunctionBase() = default; - virtual CallResult Call(FuzzData* fuzz_data) const = 0; -}; - -template -class FuzzFunction; // undefined - -// A wrapper over a fuzzed function. -template -class FuzzFunction : public FuzzFunctionBase { - public: - using Function = std::function; - FuzzFunction(Function&& function) : function_(std::move(function)) {} - // Eat necessary data in |fuzz_data| and invoke the function. - CallResult Call(FuzzData* fuzz_data) const override { - return CallWithOptionalArgs(fuzz_data->Consume>()...); - } - - private: - Function function_; - - CallResult CallWithOptionalArgs(const Optional>&... args) const { - if (!AllArgsHasValue(args...)) { - return CallResult::NOT_ENOUGH_DATA; - } - (void)function_(args.value()...); // ignore returned value - return CallResult::SUCCESS; - } -}; - // CHECK(value) << msg void CheckInternal(bool value, std::string_view msg); -// A collection of FuzzFunction's. -// FunctionsSizeType must be an integral type where -// functions_.size() <= std::numeric_limits::max(). -template -class FuzzFunctions { +// Get the oneof descriptor inside Action +const google::protobuf::OneofDescriptor* GetProtoValueDescriptor( + const google::protobuf::Descriptor* action_desc); + +template +using FunctionMapImpl = + std::map>; + +template +class FunctionMap : public FunctionMapImpl { public: - // Subclass should override this to register functions via AddFunction. - FuzzFunctions() = default; - virtual ~FuzzFunctions() = default; - // Eat some amount of data in |fuzz_data| and call one of the |functions_|. - CallResult CallOne(FuzzData* fuzz_data) const { - auto opt_number = fuzz_data->Consume(); - if (!opt_number.has_value()) { - return CallResult::NOT_ENOUGH_DATA; - } - auto function_index = opt_number.value() % functions_.size(); - return functions_[function_index]->Call(fuzz_data); + void CheckEmplace(typename FunctionMapImpl::key_type key, + typename FunctionMapImpl::mapped_type&& value) { + auto [it, inserted] = this->emplace(key, std::move(value)); + CheckInternal(inserted, + "Multiple implementation registered for tag number " + std::to_string(key)); } - - private: - template - struct FunctionTraits { - using Function = std::function; - }; - - public: - // There are no deduction guide from lambda to std::function, so the - // signature of the lambda must be specified in the template argument. - // FuzzFunctions provide the following 3 ways to specify the signature of - // the lambda: - - // AddFunction, e.g. AddFunction - template - void AddFunction(std::function&& func) { - functions_.push_back(std::make_unique>(std::move(func))); - } - - // AddFunction, e.g. AddFunction - template - void AddFunction(typename FunctionTraits::Function&& func) { - functions_.push_back(std::make_unique>(std::move(func))); - } - - // AddFunction. Equivalent to AddFunction - template - void AddFunction(typename FunctionTraits::Function&& func) { - functions_.push_back(std::make_unique>(std::move(func))); - } - - // Use |fuzz_data| as a guide to call |functions_| until |fuzz_data| is - // depleted. Return - void DepleteData(FuzzData* fuzz_data) const { - CallResult result; - while ((result = CallOne(fuzz_data)) == CallResult::SUCCESS) - ; - CheckInternal(result == CallResult::NOT_ENOUGH_DATA, - "result is " + std::to_string(static_cast(result))); - } - - protected: - // Helper for subclass to check that size of |functions_| is actually within - // SizeType. Should be called after all functions are registered. - void CheckFunctionsSize() const { - CheckInternal(functions_.size() <= std::numeric_limits::max(), - "Need to extend number of bits for function count; there are " + - std::to_string(functions_.size()) + " functions now."); - } - - private: - std::vector> functions_; }; -// An object whose APIs are being fuzzed. -template -class FuzzObject : public FuzzFunctions { - public: - // Not thread-safe; client is responsible for ensuring only one thread calls DepleteData. - void DepleteData(T* obj, FuzzData* fuzz_data) { - obj_ = obj; - FuzzFunctions::DepleteData(fuzz_data); - obj_ = nullptr; +template +int CheckConsistency() { + const auto* function_map = Action::GetFunctionMap(); + const auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor()); + + for (int field_index = 0; field_index < action_value_desc->field_count(); ++field_index) { + const auto* field_desc = action_value_desc->field(field_index); + CheckInternal(function_map->find(field_desc->number()) != function_map->end(), + "Missing impl for function " + field_desc->camelcase_name()); + } + return 0; +} + +template +void ExecuteActionProto(typename Action::Class* module, + const typename Action::Proto& action_proto) { + static auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor()); + + auto* action_refl = Action::Proto::GetReflection(); + if (!action_refl->HasOneof(action_proto, action_value_desc)) { + return; } - protected: - // Helper for subclass to get the module under test in the added functions. - T* get() const { - CheckInternal(obj_ != nullptr, "No module under test is found."); - return obj_; + const auto* field_desc = action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc); + auto number = field_desc->number(); + const auto& map = *Action::GetFunctionMap(); + auto it = map.find(number); + CheckInternal(it != map.end(), "Missing impl for function " + field_desc->camelcase_name()); + const auto& func = it->second; + func(module, action_proto, field_desc); +} + +template +void ExecuteAllActionProtos( + typename Action::Class* module, + const google::protobuf::RepeatedPtrField& action_protos) { + for (const auto& proto : action_protos) { + ExecuteActionProto(module, proto); + } +} + +// Safely cast message to T. Returns a pointer to message if cast successfully, otherwise nullptr. +template +const T* SafeCast(const google::protobuf::Message& message) { + if (message.GetDescriptor() != T::GetDescriptor()) { + return nullptr; + } + return static_cast(&message); +} + +// Cast message to const T&. Abort if type mismatch. +template +const T& CheckedCast(const google::protobuf::Message& message) { + const auto* ptr = SafeCast(message); + CheckInternal(ptr, "Cannot cast " + message.GetDescriptor()->name() + " to " + + T::GetDescriptor()->name()); + return *ptr; +} + +// A templated way to a primitive field from a message using reflection. +template +struct PrimitiveGetter; +#define FUZZ_DEFINE_PRIMITIVE_GETTER(type, func_name) \ + template <> \ + struct PrimitiveGetter { \ + static constexpr const auto fp = &google::protobuf::Reflection::func_name; \ } - private: - T* obj_ = nullptr; +FUZZ_DEFINE_PRIMITIVE_GETTER(bool, GetBool); +FUZZ_DEFINE_PRIMITIVE_GETTER(uint32_t, GetUInt32); +FUZZ_DEFINE_PRIMITIVE_GETTER(int32_t, GetInt32); +FUZZ_DEFINE_PRIMITIVE_GETTER(uint64_t, GetUInt64); +FUZZ_DEFINE_PRIMITIVE_GETTER(int64_t, GetInt64); +FUZZ_DEFINE_PRIMITIVE_GETTER(double, GetDouble); +FUZZ_DEFINE_PRIMITIVE_GETTER(float, GetFloat); + +// ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction +// with these arguments. +template +struct ActionPerfomer; // undefined + +template +struct ActionPerfomer< + FuzzFunction, void(const MessageProto&), + typename std::enable_if_t>> { + static void Invoke(typename FuzzFunction::Class* module, + const google::protobuf::Message& action_proto, + const google::protobuf::FieldDescriptor* field_desc) { + const MessageProto& arg = CheckedCast>( + action_proto.GetReflection()->GetMessage(action_proto, field_desc)); + FuzzFunction::ImplBody(module, arg); + } +}; + +template +struct ActionPerfomer>> { + static void Invoke(typename FuzzFunction::Class* module, + const google::protobuf::Message& action_proto, + const google::protobuf::FieldDescriptor* field_desc) { + Primitive arg = std::invoke(PrimitiveGetter::fp, action_proto.GetReflection(), + action_proto, field_desc); + FuzzFunction::ImplBody(module, arg); + } +}; + +template +struct ActionPerfomer { + static void Invoke(typename FuzzFunction::Class* module, const google::protobuf::Message&, + const google::protobuf::FieldDescriptor*) { + FuzzFunction::ImplBody(module); + } }; } // namespace android::fuzz + +// Fuzz existing C++ class, ClassType, with a collection of functions under the name Action. +// +// Prerequisite: ActionProto must be defined in Protobuf to describe possible actions: +// message FooActionProto { +// message NoArgs {} +// oneof value { +// bool do_foo = 1; +// NoArgs do_bar = 1; +// } +// } +// Use it to fuzz a C++ class Foo by doing the following: +// FUZZ_CLASS(Foo, FooAction) +// After linking functions of Foo to FooAction, execute all actions by: +// FooAction::ExecuteAll(foo_object, action_protos) +#define FUZZ_CLASS(ClassType, Action) \ + class Action { \ + public: \ + using Proto = Action##Proto; \ + using Class = ClassType; \ + using FunctionMap = android::fuzz::FunctionMap; \ + static FunctionMap* GetFunctionMap() { \ + static Action::FunctionMap map; \ + return ↦ \ + } \ + static void ExecuteAll(Class* module, \ + const google::protobuf::RepeatedPtrField& action_protos) { \ + [[maybe_unused]] static int consistent = android::fuzz::CheckConsistency(); \ + android::fuzz::ExecuteAllActionProtos(module, action_protos); \ + } \ + } + +#define FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) Action##_##FunctionName +#define FUZZ_FUNCTION_TAG_NAME(FunctionName) k##FunctionName + +// Implement an action defined in protobuf. Example: +// message FooActionProto { +// oneof value { +// bool do_foo = 1; +// } +// } +// class Foo { public: void DoAwesomeFoo(bool arg); }; +// FUZZ_OBJECT(FooAction, Foo); +// FUZZ_FUNCTION(FooAction, DoFoo, module, bool arg) { +// module->DoAwesomeFoo(arg); +// } +// The name DoFoo is the camel case name of the action in protobuf definition of FooActionProto. +#define FUZZ_FUNCTION(Action, FunctionName, module, ...) \ + class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) { \ + public: \ + using Class = Action::Class; \ + static void ImplBody(Action::Class*, ##__VA_ARGS__); \ + \ + private: \ + static bool registered_; \ + }; \ + auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] { \ + auto tag = Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \ + auto func = \ + &::android::fuzz::ActionPerfomer::Invoke; \ + Action::GetFunctionMap()->CheckEmplace(tag, func); \ + return true; \ + })(); \ + void FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(Action::Class* module, \ + ##__VA_ARGS__) + +// Implement a simple action by linking it to the function with the same name. Example: +// message FooActionProto { +// message NoArgs {} +// oneof value { +// NoArgs do_bar = 1; +// } +// } +// class Foo { public void DoBar(); }; +// FUZZ_OBJECT(FooAction, Foo); +// FUZZ_FUNCTION(FooAction, DoBar); +// The name DoBar is the camel case name of the action in protobuf definition of FooActionProto, and +// also the name of the function of Foo. +#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName) \ + FUZZ_FUNCTION(Action, FunctionName, module) { (void)module->FunctionName(); } diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp index 3671bb052..ef2dd9493 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp +++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include "fuzz_utils.h" @@ -31,11 +32,10 @@ using android::base::LogSeverity; using android::base::SetLogger; using android::base::StderrLogger; using android::base::StdioLogger; -using android::fuzz::Bool; -using android::fuzz::FuzzData; -using android::fuzz::FuzzObject; +using android::fuzz::CheckedCast; +using android::snapshot::SnapshotFuzzData; using android::snapshot::SnapshotFuzzEnv; -using android::snapshot::SnapshotManagerFuzzData; +using google::protobuf::RepeatedPtrField; // Avoid linking to libgsi since it needs disk I/O. namespace android::gsi { @@ -51,49 +51,49 @@ std::string GetDsuSlot(const std::string& install_dir) { namespace android::snapshot { -class FuzzSnapshotManager : public FuzzObject { - public: - FuzzSnapshotManager(); -}; +FUZZ_CLASS(ISnapshotManager, SnapshotManagerAction); -FuzzSnapshotManager::FuzzSnapshotManager() { - AddFunction([this]() { (void)get()->BeginUpdate(); }); - AddFunction([this]() { (void)get()->CancelUpdate(); }); - AddFunction([this](Bool wipe) { (void)get()->FinishedSnapshotWrites(wipe); }); - AddFunction([this]() { (void)get()->InitiateMerge(); }); - AddFunction([this](auto has_before_cancel, auto fail_before_cancel) { - std::function before_cancel; - if (has_before_cancel) { - before_cancel = [=]() { return fail_before_cancel; }; - } - (void)get()->ProcessUpdateState({}, before_cancel); - }); - AddFunction([this](auto has_progress_arg) { - double progress; - (void)get()->GetUpdateState(has_progress_arg ? &progress : nullptr); - }); - // TODO add CreateUpdateSnapshots according to proto - // TODO add MapUpdateSnapshot - // TODO add UnmapUpdateSnapshot using names from the dictionary - AddFunction([this]() { (void)get()->NeedSnapshotsInFirstStageMount(); }); - // TODO add CreateLogicalAndSnapshotPartitions - AddFunction([this](const Bool& has_callback) { - std::function callback; - if (has_callback) { - callback = []() {}; - } - (void)get()->HandleImminentDataWipe(callback); - }); - AddFunction([this]() { (void)get()->RecoveryCreateSnapshotDevices(); }); - // TODO add RecoveryCreateSnapshotDevices with metadata_device arg - AddFunction([this]() { - std::stringstream ss; - (void)get()->Dump(ss); - }); - AddFunction([this]() { (void)get()->EnsureMetadataMounted(); }); - AddFunction([this]() { (void)get()->GetSnapshotMergeStatsInstance(); }); +using ProcessUpdateStateArgs = SnapshotManagerAction::Proto::ProcessUpdateStateArgs; - CheckFunctionsSize(); +FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, BeginUpdate); +FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, CancelUpdate); +FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, InitiateMerge); +FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, NeedSnapshotsInFirstStageMount); +FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, RecoveryCreateSnapshotDevices); +FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted); +FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance); + +#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ...) \ + FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, snapshot, ##__VA_ARGS__) + +SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool wipe) { + (void)snapshot->FinishedSnapshotWrites(wipe); +} + +SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, const ProcessUpdateStateArgs& args) { + std::function before_cancel; + if (args.has_before_cancel()) { + before_cancel = [&]() { return args.fail_before_cancel(); }; + } + (void)snapshot->ProcessUpdateState({}, before_cancel); +} + +SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, bool has_progress_arg) { + double progress; + (void)snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr); +} + +SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool has_callback) { + std::function callback; + if (has_callback) { + callback = []() {}; + } + (void)snapshot->HandleImminentDataWipe(callback); +} + +SNAPSHOT_FUZZ_FUNCTION(Dump) { + std::stringstream ss; + (void)snapshot->Dump(ss); } // During global init, log all messages to stdio. This is only done once. @@ -111,31 +111,24 @@ void FatalOnlyLogger(LogId logid, LogSeverity severity, const char* tag, const c } // Stop logging (except fatal messages) after global initialization. This is only done once. int StopLoggingAfterGlobalInit() { + [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silincer; SetLogger(&FatalOnlyLogger); return 0; } } // namespace android::snapshot -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { +DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) { using namespace android::snapshot; [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit(); static SnapshotFuzzEnv env; - static FuzzSnapshotManager fuzz_snapshot_manager; [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit(); env.CheckSoftReset(); - FuzzData fuzz_data(data, size); - auto snapshot_manager_data = fuzz_data.Consume(); - if (!snapshot_manager_data.has_value()) { - return 0; - } - auto snapshot_manager = env.CheckCreateSnapshotManager(snapshot_manager_data.value()); + auto snapshot_manager = env.CheckCreateSnapshotManager(snapshot_fuzz_data); CHECK(snapshot_manager); - fuzz_snapshot_manager.DepleteData(snapshot_manager.get(), &fuzz_data); - - return 0; + SnapshotManagerAction::ExecuteAll(snapshot_manager.get(), snapshot_fuzz_data.actions()); } diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp index 7829193f5..dfac3ef62 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp +++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp @@ -250,16 +250,16 @@ std::unique_ptr SnapshotFuzzEnv::CheckMapSuper(const std::string& fa } std::unique_ptr SnapshotFuzzEnv::CheckCreateSnapshotManager( - const SnapshotManagerFuzzData& data) { + const SnapshotFuzzData& data) { auto partition_opener = std::make_unique(super()); auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata"; PCHECK(Mkdir(metadata_dir)); - auto device_info = new SnapshotFuzzDeviceInfo(data.device_info_data, + auto device_info = new SnapshotFuzzDeviceInfo(data.device_info_data(), std::move(partition_opener), metadata_dir); auto snapshot = SnapshotManager::New(device_info /* takes ownership */); snapshot->images_ = CheckCreateFakeImageManager(fake_root_->tmp_path()); - snapshot->has_local_image_manager_ = data.is_local_image_manager; + snapshot->has_local_image_manager_ = data.manager_data().is_local_image_manager(); return snapshot; } diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h index 8c0d5dd54..3f3c992b8 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h +++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h @@ -24,23 +24,10 @@ // libsnapshot-specific code for fuzzing. Defines fake classes that are depended // by SnapshotManager. +#include "android/snapshot/snapshot_fuzz.pb.h" + namespace android::snapshot { -// Controls the behavior of IDeviceInfo. -typedef struct SnapshotFuzzDeviceInfoData { - bool slot_suffix_is_a : 1; - bool is_overlayfs_setup : 1; - bool allow_set_boot_control_merge_status : 1; - bool allow_set_slot_as_unbootable : 1; - bool is_recovery : 1; -} __attribute__((packed)) SnapshotFuzzDeviceInfoData; - -// Controls the behavior of the test SnapshotManager. -typedef struct SnapshotManagerFuzzData { - SnapshotFuzzDeviceInfoData device_info_data; - bool is_local_image_manager : 1; -} __attribute__((packed)) SnapshotManagerFuzzData; - class AutoMemBasedDir; // Prepare test environment. This has a heavy overhead and should be done once. @@ -60,8 +47,7 @@ class SnapshotFuzzEnv { // Create a snapshot manager for this test run. // Client is responsible for maintaining the lifetime of |data| over the life time of // ISnapshotManager. - std::unique_ptr CheckCreateSnapshotManager( - const SnapshotManagerFuzzData& data); + std::unique_ptr CheckCreateSnapshotManager(const SnapshotFuzzData& data); // Return path to super partition. const std::string& super() const; @@ -82,10 +68,10 @@ class SnapshotFuzzEnv { class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo { public: // Client is responsible for maintaining the lifetime of |data|. - SnapshotFuzzDeviceInfo(const SnapshotFuzzDeviceInfoData& data, + SnapshotFuzzDeviceInfo(const FuzzDeviceInfoData& data, std::unique_ptr&& partition_opener, const std::string& metadata_dir) - : data_(data), + : data_(&data), partition_opener_(std::move(partition_opener)), metadata_dir_(metadata_dir) {} @@ -101,17 +87,21 @@ class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo { } // Following APIs are fuzzed. - std::string GetSlotSuffix() const override { return data_.slot_suffix_is_a ? "_a" : "_b"; } - std::string GetOtherSlotSuffix() const override { return data_.slot_suffix_is_a ? "_b" : "_a"; } - bool IsOverlayfsSetup() const override { return data_.is_overlayfs_setup; } - bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override { - return data_.allow_set_boot_control_merge_status; + std::string GetSlotSuffix() const override { return data_->slot_suffix_is_a() ? "_a" : "_b"; } + std::string GetOtherSlotSuffix() const override { + return data_->slot_suffix_is_a() ? "_b" : "_a"; } - bool SetSlotAsUnbootable(unsigned int) override { return data_.allow_set_slot_as_unbootable; } - bool IsRecovery() const override { return data_.is_recovery; } + bool IsOverlayfsSetup() const override { return data_->is_overlayfs_setup(); } + bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override { + return data_->allow_set_boot_control_merge_status(); + } + bool SetSlotAsUnbootable(unsigned int) override { + return data_->allow_set_slot_as_unbootable(); + } + bool IsRecovery() const override { return data_->is_recovery(); } private: - SnapshotFuzzDeviceInfoData data_; + const FuzzDeviceInfoData* data_; std::unique_ptr partition_opener_; std::string metadata_dir_; }; From c0df932a43c579059ace8e289b842799ef5c3c02 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Thu, 30 Apr 2020 14:22:07 -0700 Subject: [PATCH 3/8] libsnapshot_fuzzer: add additional tests for more APIs Test: run it Bug: 154633114 Change-Id: I956cb74bfd46750137dfa73e9e040dd9d1782ce7 --- .../android/snapshot/snapshot_fuzz.proto | 18 ++++--- fs_mgr/libsnapshot/fuzz_utils.h | 12 +++++ fs_mgr/libsnapshot/snapshot_fuzz.cpp | 47 ++++++++++++++++--- fs_mgr/libsnapshot/snapshot_fuzz_utils.h | 5 ++ 4 files changed, 70 insertions(+), 12 deletions(-) diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto index 679213c47..2df54e28c 100644 --- a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto +++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto @@ -40,16 +40,19 @@ message SnapshotManagerActionProto { bool has_before_cancel = 1; bool fail_before_cancel = 2; } + message CreateLogicalAndSnapshotPartitionsArgs { + bool use_correct_super = 1; + string super = 2; + int64 timeout_millis = 3; + } + message RecoveryCreateSnapshotDevicesArgs { + bool has_metadata_device_object = 1; + bool metadata_mounted = 2; + } reserved 7; reserved "create_update_snapshots"; reserved 8; reserved "map_update_snapshot"; - reserved 9; - reserved "unmap_update_snapshot"; - reserved 11; - reserved "create_logical_and_snapshot_partitions"; - reserved 14; - reserved "recovery_create_snapshot_devices_with_metadata"; oneof value { NoArgs begin_update = 1; NoArgs cancel_update = 2; @@ -57,9 +60,12 @@ message SnapshotManagerActionProto { NoArgs initiate_merge = 4; ProcessUpdateStateArgs process_update_state = 5; bool get_update_state = 6; + string unmap_update_snapshot = 9; NoArgs need_snapshots_in_first_stage_mount = 10; + CreateLogicalAndSnapshotPartitionsArgs create_logical_and_snapshot_partitions = 11; bool handle_imminent_data_wipe = 12; NoArgs recovery_create_snapshot_devices = 13; + RecoveryCreateSnapshotDevicesArgs recovery_create_snapshot_devices_with_metadata = 14; NoArgs dump = 15; NoArgs ensure_metadata_mounted = 16; NoArgs get_snapshot_merge_stats_instance = 17; diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h index 8504d7bd1..4dc6cdc0b 100644 --- a/fs_mgr/libsnapshot/fuzz_utils.h +++ b/fs_mgr/libsnapshot/fuzz_utils.h @@ -169,6 +169,18 @@ struct ActionPerfomer { } }; +template +struct ActionPerfomer { + static void Invoke(typename FuzzFunction::Class* module, + const google::protobuf::Message& action_proto, + const google::protobuf::FieldDescriptor* field_desc) { + std::string scratch; + const std::string& arg = action_proto.GetReflection()->GetStringReference( + action_proto, field_desc, &scratch); + FuzzFunction::ImplBody(module, arg); + } +}; + } // namespace android::fuzz // Fuzz existing C++ class, ClassType, with a collection of functions under the name Action. diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp index ef2dd9493..3e02ba7e8 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp +++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp @@ -51,9 +51,15 @@ std::string GetDsuSlot(const std::string& install_dir) { namespace android::snapshot { +SnapshotFuzzEnv* GetSnapshotFuzzEnv(); + FUZZ_CLASS(ISnapshotManager, SnapshotManagerAction); using ProcessUpdateStateArgs = SnapshotManagerAction::Proto::ProcessUpdateStateArgs; +using CreateLogicalAndSnapshotPartitionsArgs = + SnapshotManagerAction::Proto::CreateLogicalAndSnapshotPartitionsArgs; +using RecoveryCreateSnapshotDevicesArgs = + SnapshotManagerAction::Proto::RecoveryCreateSnapshotDevicesArgs; FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, BeginUpdate); FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, CancelUpdate); @@ -96,6 +102,31 @@ SNAPSHOT_FUZZ_FUNCTION(Dump) { (void)snapshot->Dump(ss); } +SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, const std::string& name) { + (void)snapshot->UnmapUpdateSnapshot(name); +} + +SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, + const CreateLogicalAndSnapshotPartitionsArgs& args) { + const std::string* super; + if (args.use_correct_super()) { + super = &GetSnapshotFuzzEnv()->super(); + } else { + super = &args.super(); + } + (void)snapshot->CreateLogicalAndSnapshotPartitions( + *super, std::chrono::milliseconds(args.timeout_millis())); +} + +SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, + const RecoveryCreateSnapshotDevicesArgs& args) { + std::unique_ptr device; + if (args.has_metadata_device_object()) { + device = std::make_unique(args.metadata_mounted()); + } + (void)snapshot->RecoveryCreateSnapshotDevices(device); +} + // During global init, log all messages to stdio. This is only done once. int AllowLoggingDuringGlobalInit() { SetLogger(&StdioLogger); @@ -116,18 +147,22 @@ int StopLoggingAfterGlobalInit() { return 0; } +SnapshotFuzzEnv* GetSnapshotFuzzEnv() { + [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit(); + static SnapshotFuzzEnv env; + [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit(); + return &env; +} + } // namespace android::snapshot DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) { using namespace android::snapshot; - [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit(); - static SnapshotFuzzEnv env; - [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit(); + auto env = GetSnapshotFuzzEnv(); + env->CheckSoftReset(); - env.CheckSoftReset(); - - auto snapshot_manager = env.CheckCreateSnapshotManager(snapshot_fuzz_data); + auto snapshot_manager = env->CheckCreateSnapshotManager(snapshot_fuzz_data); CHECK(snapshot_manager); SnapshotManagerAction::ExecuteAll(snapshot_manager.get(), snapshot_fuzz_data.actions()); diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h index 3f3c992b8..66a618118 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h +++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h @@ -30,6 +30,11 @@ namespace android::snapshot { class AutoMemBasedDir; +class DummyAutoDevice : public AutoDevice { + public: + DummyAutoDevice(bool mounted) : AutoDevice(mounted ? "dummy" : "") {} +}; + // Prepare test environment. This has a heavy overhead and should be done once. class SnapshotFuzzEnv { public: From 74d1fb4571b519e5fa1477df39b33d63238ce9e1 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Wed, 29 Apr 2020 17:57:58 -0700 Subject: [PATCH 4/8] libsnapshot_fuzzer: Fuzz CreateUpdateSnapshots Test: run it Bug: 154633114 Change-Id: I56ed2953e85714d23a9273224a28eb8e8e47a54d --- fs_mgr/libsnapshot/Android.bp | 12 ++- .../android/snapshot/snapshot_fuzz.proto | 5 +- fs_mgr/libsnapshot/snapshot_fuzz.cpp | 5 ++ .../update_engine/update_metadata.proto | 75 +++++++++++++++++++ 4 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 fs_mgr/libsnapshot/update_engine/update_metadata.proto diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index c1213f67f..00991f4d1 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -256,30 +256,38 @@ cc_fuzz { srcs: [ // Compile the protobuf definition again with type full. "android/snapshot/snapshot_fuzz.proto", + "update_engine/update_metadata.proto", "fuzz_utils.cpp", "snapshot_fuzz.cpp", "snapshot_fuzz_utils.cpp", + + // Compile libsnapshot sources directly to avoid dependency + // to update_metadata-protos + ":libsnapshot_sources", ], static_libs: [ "libbase", "libcrypto_static", "libcutils", + "libext2_uuid", + "libext4_utils", + "libfstab", "libfs_mgr", "libgtest", // from libsnapshot_test_helpers "libgmock", // from libsnapshot_test_helpers "liblog", "liblp", - "libsnapshot_init", // don't use binder or hwbinder "libsnapshot_test_helpers", "libprotobuf-mutator", - "update_metadata-protos", ], header_libs: [ + "libfiemap_headers", "libstorage_literals_headers", ], proto: { type: "full", canonical_path_from_root: false, + local_include_dirs: ["."], }, fuzz_config: { diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto index 2df54e28c..7e8559b68 100644 --- a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto +++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto @@ -15,6 +15,8 @@ syntax = "proto3"; package android.snapshot; +import "update_engine/update_metadata.proto"; + // Controls the behavior of IDeviceInfo. // Next: 6 message FuzzDeviceInfoData { @@ -49,8 +51,6 @@ message SnapshotManagerActionProto { bool has_metadata_device_object = 1; bool metadata_mounted = 2; } - reserved 7; - reserved "create_update_snapshots"; reserved 8; reserved "map_update_snapshot"; oneof value { @@ -60,6 +60,7 @@ message SnapshotManagerActionProto { NoArgs initiate_merge = 4; ProcessUpdateStateArgs process_update_state = 5; bool get_update_state = 6; + chromeos_update_engine.DeltaArchiveManifest create_update_snapshots = 7; string unmap_update_snapshot = 9; NoArgs need_snapshots_in_first_stage_mount = 10; CreateLogicalAndSnapshotPartitionsArgs create_logical_and_snapshot_partitions = 11; diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp index 3e02ba7e8..ece1c067e 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp +++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp @@ -35,6 +35,7 @@ using android::base::StdioLogger; using android::fuzz::CheckedCast; using android::snapshot::SnapshotFuzzData; using android::snapshot::SnapshotFuzzEnv; +using chromeos_update_engine::DeltaArchiveManifest; using google::protobuf::RepeatedPtrField; // Avoid linking to libgsi since it needs disk I/O. @@ -102,6 +103,10 @@ SNAPSHOT_FUZZ_FUNCTION(Dump) { (void)snapshot->Dump(ss); } +SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, const DeltaArchiveManifest& manifest) { + (void)snapshot->CreateUpdateSnapshots(manifest); +} + SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, const std::string& name) { (void)snapshot->UnmapUpdateSnapshot(name); } diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto new file mode 100644 index 000000000..be5e1fe69 --- /dev/null +++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto @@ -0,0 +1,75 @@ +// +// Copyright (C) 2020 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. +// + +// A subset of system/update_engine/update_metadata.proto. A separate file is +// used here because: +// - The original file is optimized for LITE_RUNTIME, but fuzzing needs +// reflection. +// - The definition here has less fields. libsnapshot only uses fields declared +// here, and all fields declared here are fuzzed by libsnapshot_fuzzer. If +// libsnapshot uses more fields in system/update_engine/update_metadata.proto +// in the future, they must be added here too, otherwise it will fail to +// compile. +// +// It is okay that this file is older than +// system/update_engine/update_metadata.proto as long as the messages defined +// here can also be parsed by protobuf defined there. However, it is not +// okay to add fields here without adding them to +// system/update_engine/update_metadata.proto. Doing so will cause a compiler +// error when libsnapshot code starts to use these dangling fields. + +syntax = "proto2"; + +package chromeos_update_engine; + +message Extent { + optional uint64 start_block = 1; + optional uint64 num_blocks = 2; +} + +message PartitionInfo { + optional uint64 size = 1; +} + +message InstallOperation { + enum Type { SOURCE_COPY = 4; } + required Type type = 1; + repeated Extent src_extents = 4; + repeated Extent dst_extents = 6; +} + +message PartitionUpdate { + required string partition_name = 1; + optional PartitionInfo new_partition_info = 7; + repeated InstallOperation operations = 8; + optional Extent hash_tree_extent = 11; + optional Extent fec_extent = 15; +} + +message DynamicPartitionGroup { + required string name = 1; + optional uint64 size = 2; + repeated string partition_names = 3; +} + +message DynamicPartitionMetadata { + repeated DynamicPartitionGroup groups = 1; +} + +message DeltaArchiveManifest { + repeated PartitionUpdate partitions = 13; + optional DynamicPartitionMetadata dynamic_partition_metadata = 15; +} From db0e62b87ad391fcacfdf13136326b8512bf36b8 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Thu, 30 Apr 2020 17:06:37 -0700 Subject: [PATCH 5/8] libsnapshot_fuzzer: construct valid super partition metadata. This should hopefully achieve more coverage. Test: pass Bug: 154633114 Change-Id: Ice575f2d8c3e22b80465c133d055e7c4368ebdfa --- .../android/snapshot/snapshot_fuzz.proto | 10 ++++- fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp | 43 +++++++++++++++++++ fs_mgr/libsnapshot/snapshot_fuzz_utils.h | 5 +++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto index 7e8559b68..77b224517 100644 --- a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto +++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto @@ -77,7 +77,15 @@ message SnapshotManagerActionProto { message SnapshotFuzzData { FuzzDeviceInfoData device_info_data = 1; FuzzSnapshotManagerData manager_data = 2; + + // If true: + // - if super_data is empty, create empty super partition metadata. + // - otherwise, create super partition metadata accordingly. + // If false, no valid super partition metadata (it is zeroed) + bool is_super_metadata_valid = 3; + chromeos_update_engine.DeltaArchiveManifest super_data = 4; + // More data used to prep the test before running actions. - reserved 3 to 9999; + reserved 5 to 9999; repeated SnapshotManagerActionProto actions = 10000; } diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp index dfac3ef62..8101d03b7 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp +++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp @@ -47,6 +47,9 @@ using android::base::WriteStringToFile; using android::dm::LoopControl; using android::fiemap::IImageManager; using android::fiemap::ImageManager; +using android::fs_mgr::BlockDeviceInfo; +using android::fs_mgr::IPartitionOpener; +using chromeos_update_engine::DynamicPartitionMetadata; // This directory is exempted from pinning in ImageManager. static const char MNT_DIR[] = "/data/gsi/ota/test/"; @@ -252,6 +255,7 @@ std::unique_ptr SnapshotFuzzEnv::CheckMapSuper(const std::string& fa std::unique_ptr SnapshotFuzzEnv::CheckCreateSnapshotManager( const SnapshotFuzzData& data) { auto partition_opener = std::make_unique(super()); + CheckWriteSuperMetadata(data, *partition_opener); auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata"; PCHECK(Mkdir(metadata_dir)); @@ -268,4 +272,43 @@ const std::string& SnapshotFuzzEnv::super() const { return fake_super_; } +void SnapshotFuzzEnv::CheckWriteSuperMetadata(const SnapshotFuzzData& data, + const IPartitionOpener& opener) { + if (!data.is_super_metadata_valid()) { + // Leave it zero. + return; + } + + BlockDeviceInfo super_device("super", SUPER_IMAGE_SIZE, 0, 0, 4096); + std::vector devices = {super_device}; + auto builder = MetadataBuilder::New(devices, "super", 65536, 2); + CHECK(builder != nullptr); + + // Attempt to create a super partition metadata using proto. All errors are ignored. + for (const auto& group_proto : data.super_data().dynamic_partition_metadata().groups()) { + (void)builder->AddGroup(group_proto.name(), group_proto.size()); + for (const auto& partition_name : group_proto.partition_names()) { + (void)builder->AddPartition(partition_name, group_proto.name(), + LP_PARTITION_ATTR_READONLY); + } + } + + for (const auto& partition_proto : data.super_data().partitions()) { + auto p = builder->FindPartition(partition_proto.partition_name()); + if (p == nullptr) continue; + (void)builder->ResizePartition(p, partition_proto.new_partition_info().size()); + } + + auto metadata = builder->Export(); + // metadata may be nullptr if it is not valid (e.g. partition name too long). + // In this case, just use empty super partition data. + if (metadata == nullptr) { + builder = MetadataBuilder::New(devices, "super", 65536, 2); + CHECK(builder != nullptr); + metadata = builder->Export(); + CHECK(metadata != nullptr); + } + CHECK(FlashPartitionTable(opener, super(), *metadata.get())); +} + } // namespace android::snapshot diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h index 66a618118..5533defa7 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h +++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h @@ -16,8 +16,10 @@ #include #include +#include #include #include +#include #include #include @@ -68,6 +70,9 @@ class SnapshotFuzzEnv { static std::unique_ptr CheckMapSuper(const std::string& fake_persist_path, android::dm::LoopControl* control, std::string* fake_super); + + void CheckWriteSuperMetadata(const SnapshotFuzzData& proto, + const android::fs_mgr::IPartitionOpener& opener); }; class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo { From 0d4a47b0c70dce693f3b25c56a5da9cf75cef5a7 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Thu, 30 Apr 2020 17:56:48 -0700 Subject: [PATCH 6/8] libsnapshot_fuzzer: Fuzz MapUpdateSnapshot. Test: run it Bug: 154633114 Change-Id: I15ea0fb28df5b0f6d32096aab808549c3855c289 --- .../android/snapshot/snapshot_fuzz.proto | 16 ++++++++++++-- .../include/libsnapshot/snapshot.h | 1 + fs_mgr/libsnapshot/snapshot_fuzz.cpp | 21 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto index 77b224517..91fbb60eb 100644 --- a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto +++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto @@ -33,6 +33,19 @@ message FuzzSnapshotManagerData { bool is_local_image_manager = 1; } +// A simplified version of CreateLogicalPartitionParams for fuzzing. +// Next: 9 +message CreateLogicalPartitionParamsProto { + bool use_correct_super = 1; + string block_device = 2; + bool has_metadata_slot = 3; + uint32 metadata_slot = 4; + string partition_name = 5; + bool force_writable = 6; + int64 timeout_millis = 7; + string device_name = 8; +} + // Mimics the API of ISnapshotManager. Defines one action on the snapshot // manager. // Next: 18 @@ -51,8 +64,6 @@ message SnapshotManagerActionProto { bool has_metadata_device_object = 1; bool metadata_mounted = 2; } - reserved 8; - reserved "map_update_snapshot"; oneof value { NoArgs begin_update = 1; NoArgs cancel_update = 2; @@ -61,6 +72,7 @@ message SnapshotManagerActionProto { ProcessUpdateStateArgs process_update_state = 5; bool get_update_state = 6; chromeos_update_engine.DeltaArchiveManifest create_update_snapshots = 7; + CreateLogicalPartitionParamsProto map_update_snapshot = 8; string unmap_update_snapshot = 9; NoArgs need_snapshots_in_first_stage_mount = 10; CreateLogicalAndSnapshotPartitionsArgs create_logical_and_snapshot_partitions = 11; diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index 808186618..4658fb432 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -173,6 +173,7 @@ class ISnapshotManager { // Map a snapshotted partition for OTA clients to write to. Write-protected regions are // determined previously in CreateSnapshots. + // |snapshot_path| must not be nullptr. virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params, std::string* snapshot_path) = 0; diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp index ece1c067e..7b57e79c3 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp +++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp @@ -32,6 +32,7 @@ using android::base::LogSeverity; using android::base::SetLogger; using android::base::StderrLogger; using android::base::StdioLogger; +using android::fs_mgr::CreateLogicalPartitionParams; using android::fuzz::CheckedCast; using android::snapshot::SnapshotFuzzData; using android::snapshot::SnapshotFuzzEnv; @@ -132,6 +133,26 @@ SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, (void)snapshot->RecoveryCreateSnapshotDevices(device); } +SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, const CreateLogicalPartitionParamsProto& params_proto) { + auto partition_opener = std::make_unique(GetSnapshotFuzzEnv()->super()); + CreateLogicalPartitionParams params; + if (params_proto.use_correct_super()) { + params.block_device = GetSnapshotFuzzEnv()->super(); + } else { + params.block_device = params_proto.block_device(); + } + if (params_proto.has_metadata_slot()) { + params.metadata_slot = params_proto.metadata_slot(); + } + params.partition_name = params_proto.partition_name(); + params.force_writable = params_proto.force_writable(); + params.timeout_ms = std::chrono::milliseconds(params_proto.timeout_millis()); + params.device_name = params_proto.device_name(); + params.partition_opener = partition_opener.get(); + std::string path; + (void)snapshot->MapUpdateSnapshot(params, &path); +} + // During global init, log all messages to stdio. This is only done once. int AllowLoggingDuringGlobalInit() { SetLogger(&StdioLogger); From 4d5bfabfeb0f997e2a4b44ee7136a6d201b57de4 Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Fri, 1 May 2020 10:50:25 -0700 Subject: [PATCH 7/8] Also log corpus when aborted through libbase. If a CHECK or LOG(FATAL) is hit, also attempt to log the current corpus. Test: pass Bug: 154633114 Change-Id: Id0f376021011924f5d64eb5b591b5ebab6dc7dbc --- fs_mgr/libsnapshot/snapshot_fuzz.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp index 7b57e79c3..421154d3c 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp +++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp @@ -53,6 +53,8 @@ std::string GetDsuSlot(const std::string& install_dir) { namespace android::snapshot { +const SnapshotFuzzData* current_data = nullptr; + SnapshotFuzzEnv* GetSnapshotFuzzEnv(); FUZZ_CLASS(ISnapshotManager, SnapshotManagerAction); @@ -164,6 +166,22 @@ void FatalOnlyLogger(LogId logid, LogSeverity severity, const char* tag, const c unsigned int line, const char* message) { if (severity == LogSeverity::FATAL) { StderrLogger(logid, severity, tag, file, line, message); + + // If test fails by a LOG(FATAL) or CHECK(), log the corpus. If it abort()'s, there's + // nothing else we can do. + StderrLogger(logid, severity, tag, __FILE__, __LINE__, + "Attempting to dump current corpus:"); + if (current_data == nullptr) { + StderrLogger(logid, severity, tag, __FILE__, __LINE__, "Current corpus is nullptr."); + } else { + std::string content; + if (!google::protobuf::TextFormat::PrintToString(*current_data, &content)) { + StderrLogger(logid, severity, tag, __FILE__, __LINE__, + "Failed to print corpus to string."); + } else { + StderrLogger(logid, severity, tag, __FILE__, __LINE__, content.c_str()); + } + } } } // Stop logging (except fatal messages) after global initialization. This is only done once. @@ -185,6 +203,8 @@ SnapshotFuzzEnv* GetSnapshotFuzzEnv() { DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) { using namespace android::snapshot; + current_data = &snapshot_fuzz_data; + auto env = GetSnapshotFuzzEnv(); env->CheckSoftReset(); From c43f513c9105e297e61f83db5dd1a62e70d63e0d Mon Sep 17 00:00:00 2001 From: Yifan Hong Date: Thu, 30 Apr 2020 23:04:07 -0700 Subject: [PATCH 8/8] Enable automatic libsnapshot fuzzer runs Test: none Bug: 154633114 Change-Id: I226d88b74bd1ea6b3cb912609d7bdf10aa8208fd --- fs_mgr/libsnapshot/Android.bp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index 00991f4d1..e916693ea 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -295,7 +295,6 @@ cc_fuzz { componentid: 30545, hotlists: ["1646452"], fuzz_on_haiku_host: false, - // TODO(b/154633114): set to true to run this automatically. - fuzz_on_haiku_device: false, + fuzz_on_haiku_device: true, }, }