diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index c2f435ffd..3bfa4d574 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -366,80 +366,6 @@ cc_test { gtest: false, } -cc_defaults { - name: "libsnapshot_fuzzer_defaults", - defaults: [ - "libsnapshot_cow_defaults", - ], - native_coverage : true, - 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", - "libbrotli", - "libc++fs", - "libchrome", - "libcrypto_static", - "libcutils", - "libext2_uuid", - "libext4_utils", - "libfstab", - "libfs_mgr", - "libgtest", // from libsnapshot_test_helpers - "libgmock", // from libsnapshot_test_helpers - "liblog", - "liblp", - "libsnapshot_cow", - "libsnapshot_test_helpers", - "libprotobuf-mutator", - "libz", - ], - header_libs: [ - "libfiemap_headers", - "libstorage_literals_headers", - "libupdate_engine_headers", - ], - proto: { - type: "full", - canonical_path_from_root: false, - local_include_dirs: ["."], - }, -} - -cc_fuzz { - name: "libsnapshot_fuzzer", - defaults: ["libsnapshot_fuzzer_defaults"], - corpus: ["corpus/*"], - fuzz_config: { - cc: ["android-virtual-ab+bugs@google.com"], - componentid: 30545, - hotlists: ["1646452"], - fuzz_on_haiku_host: false, - fuzz_on_haiku_device: true, - }, -} - -cc_test { - name: "libsnapshot_fuzzer_test", - defaults: ["libsnapshot_fuzzer_defaults"], - data: ["corpus/*"], - test_suites: [ - "device-tests", - ], - auto_gen_config: true, - require_root: true, -} - cc_test { name: "cow_api_test", defaults: [ diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto deleted file mode 100644 index a55b42acb..000000000 --- a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto +++ /dev/null @@ -1,110 +0,0 @@ -// 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; - -import "update_engine/update_metadata.proto"; - -// 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; -} - -// 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 -message SnapshotManagerActionProto { - message NoArgs {} - message ProcessUpdateStateArgs { - 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 18 to 9999; - 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; - 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; - 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; - - // Test directives that has nothing to do with ISnapshotManager API surface. - NoArgs switch_slot = 10000; - } -} - -// Includes all data that needs to be fuzzed. -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; - - // Whether the directory that mocks /metadata/ota/snapshot is created. - bool has_metadata_snapshots_dir = 5; - - // More data used to prep the test before running actions. - reserved 6 to 9999; - repeated SnapshotManagerActionProto actions = 10000; -} diff --git a/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt b/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt deleted file mode 100644 index c474f4ccf..000000000 --- a/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt +++ /dev/null @@ -1,41 +0,0 @@ -device_info_data { - allow_set_slot_as_unbootable: true - is_recovery: true -} -is_super_metadata_valid: true -super_data { - partitions { - partition_name: "sys_a" - new_partition_info { - size: 3145728 - } - } - partitions { - partition_name: "vnnd_" - new_partition_info { - size: 3145728 - } - } - partitions { - partition_name: "prd_a" - new_partition_info { - } - } - dynamic_partition_metadata { - groups { - name: "group_google_dp_a" - size: 34375467008 - partition_names: "sys_a" - partition_names: "vnd_a" - partition_names: "prd_a" - } - } -} -has_metadata_snapshots_dir: true -actions { - handle_imminent_data_wipe: true -} -actions { - begin_update { - } -} diff --git a/fs_mgr/libsnapshot/corpus/launch_device.txt b/fs_mgr/libsnapshot/corpus/launch_device.txt deleted file mode 100644 index 55a7f2ccd..000000000 --- a/fs_mgr/libsnapshot/corpus/launch_device.txt +++ /dev/null @@ -1,161 +0,0 @@ -device_info_data { - slot_suffix_is_a: true - is_overlayfs_setup: false - allow_set_boot_control_merge_status: true - allow_set_slot_as_unbootable: true - is_recovery: false -} -manager_data { - is_local_image_manager: false -} -is_super_metadata_valid: true -super_data { - partitions { - partition_name: "sys_a" - new_partition_info { - size: 3145728 - } - } - partitions { - partition_name: "vnd_a" - new_partition_info { - size: 3145728 - } - } - partitions { - partition_name: "prd_a" - new_partition_info { - size: 3145728 - } - } - dynamic_partition_metadata { - groups { - name: "group_google_dp_a" - size: 15728640 - partition_names: "sys_a" - partition_names: "vnd_a" - partition_names: "prd_a" - } - } -} -has_metadata_snapshots_dir: true -actions { - begin_update { - } -} -actions { - create_update_snapshots { - partitions { - partition_name: "sys" - new_partition_info { - size: 3878912 - } - operations { - type: ZERO, - dst_extents { - start_block: 0 - num_blocks: 947 - } - } - } - partitions { - partition_name: "vnd" - new_partition_info { - size: 3878912 - } - operations { - type: ZERO, - dst_extents { - start_block: 0 - num_blocks: 947 - } - } - } - partitions { - partition_name: "prd" - new_partition_info { - size: 3878912 - } - operations { - type: ZERO, - dst_extents { - start_block: 0 - num_blocks: 947 - } - } - } - dynamic_partition_metadata { - groups { - name: "group_google_dp" - size: 15728640 - partition_names: "sys" - partition_names: "vnd" - partition_names: "prd" - } - } - } -} -actions { - map_update_snapshot { - use_correct_super: true - has_metadata_slot: true - metadata_slot: 1 - partition_name: "sys_b" - force_writable: true - timeout_millis: 3000 - } -} -actions { - map_update_snapshot { - use_correct_super: true - has_metadata_slot: true - metadata_slot: 1 - partition_name: "vnd_b" - force_writable: true - timeout_millis: 3000 - } -} -actions { - map_update_snapshot { - use_correct_super: true - has_metadata_slot: true - metadata_slot: 1 - partition_name: "prd_b" - force_writable: true - timeout_millis: 3000 - } -} -actions { - finished_snapshot_writes: false -} -actions { - unmap_update_snapshot: "sys_b" -} -actions { - unmap_update_snapshot: "vnd_b" -} -actions { - unmap_update_snapshot: "prd_b" -} -actions { - switch_slot { - } -} -actions { - need_snapshots_in_first_stage_mount { - } -} -actions { - create_logical_and_snapshot_partitions { - use_correct_super: true - timeout_millis: 5000 - } -} -actions { - initiate_merge { - } -} -actions { - process_update_state { - } -} diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh deleted file mode 100755 index 5995cefde..000000000 --- a/fs_mgr/libsnapshot/fuzz.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash -PROJECT_PATH=system/core/fs_mgr/libsnapshot -FUZZ_TARGET=libsnapshot_fuzzer -TARGET_ARCH=$(get_build_var TARGET_ARCH) -FUZZ_BINARY=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET} -DEVICE_INIT_CORPUS_DIR=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/corpus -DEVICE_GENERATED_CORPUS_DIR=/data/local/tmp/${FUZZ_TARGET}/corpus -DEVICE_GCOV_DIR=/data/local/tmp/${FUZZ_TARGET}/gcov -HOST_SCRATCH_DIR=/tmp/${FUZZ_TARGET} -GCOV_TOOL=${HOST_SCRATCH_DIR}/llvm-gcov - -build_normal() ( - pushd $(gettop) - NATIVE_COVERAGE="" NATIVE_LINE_COVERAGE="" NATIVE_COVERAGE_PATHS="" m ${FUZZ_TARGET} - ret=$? - popd - return ${ret} -) - -build_cov() { - pushd $(gettop) - NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" NATIVE_COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET} - ret=$? - popd - return ${ret} -} - -prepare_device() { - adb root && adb remount && - adb shell mkdir -p ${DEVICE_GENERATED_CORPUS_DIR} && - adb shell rm -rf ${DEVICE_GCOV_DIR} && - adb shell mkdir -p ${DEVICE_GCOV_DIR} -} - -push_binary() { - adb push ${ANDROID_PRODUCT_OUT}/${FUZZ_BINARY} ${FUZZ_BINARY} && - adb push ${ANDROID_PRODUCT_OUT}/${DEVICE_INIT_CORPUS_DIR} $(dirname ${FUZZ_BINARY}) -} - -prepare_host() { - which lcov || { - echo "please run:"; - echo " sudo apt-get install lcov "; - return 1; - } - rm -rf ${HOST_SCRATCH_DIR} && - mkdir -p ${HOST_SCRATCH_DIR} -} - -# run_snapshot_fuzz -runs=10000 -generate_corpus() { - [[ "$@" ]] || { echo "run with -runs=X"; return 1; } - - prepare_device && - build_normal && - push_binary && - adb shell ${FUZZ_BINARY} "$@" ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR} -} - -run_snapshot_fuzz() { - prepare_device && - build_cov && - push_binary && - adb shell GCOV_PREFIX=${DEVICE_GCOV_DIR} GCOV_PREFIX_STRIP=3 \ - ${FUZZ_BINARY} \ - -runs=0 \ - ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR} -} - -show_fuzz_result() { - prepare_host && - unzip -o -j -d ${HOST_SCRATCH_DIR} ${ANDROID_PRODUCT_OUT}/coverage/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}.zip && - adb shell find ${DEVICE_GCOV_DIR} -type f | xargs -I {} adb pull {} ${HOST_SCRATCH_DIR} && - ls ${HOST_SCRATCH_DIR} && - cat > ${GCOV_TOOL} <<< ' -#!/bin/bash -exec llvm-cov gcov "$@" -' && - chmod +x ${GCOV_TOOL} && - lcov --directory ${HOST_SCRATCH_DIR} --base-directory $(gettop) --gcov-tool ${GCOV_TOOL} --capture -o ${HOST_SCRATCH_DIR}/report.cov && - genhtml ${HOST_SCRATCH_DIR}/report.cov -o ${HOST_SCRATCH_DIR}/html && - echo file://$(realpath ${HOST_SCRATCH_DIR}/html/index.html) -} - -# run_snapshot_fuzz -runs=10000 -run_snapshot_fuzz_all() { - generate_corpus "$@" && - run_snapshot_fuzz && - show_fuzz_result -} diff --git a/fs_mgr/libsnapshot/fuzz_utils.cpp b/fs_mgr/libsnapshot/fuzz_utils.cpp deleted file mode 100644 index 0263f7e46..000000000 --- a/fs_mgr/libsnapshot/fuzz_utils.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// 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. - -#include "fuzz_utils.h" - -#include - -namespace android::fuzz { - -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 deleted file mode 100644 index 20b13b2fa..000000000 --- a/fs_mgr/libsnapshot/fuzz_utils.h +++ /dev/null @@ -1,285 +0,0 @@ -// 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. - -#pragma once - -#include -#include -#include - -#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 { - -// CHECK(value) << msg -void CheckInternal(bool value, std::string_view msg); - -// 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: - 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)); - } -}; - -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; -} - -// Get the field descriptor for the oneof field in the action message. If no oneof field is set, -// return nullptr. -template -const google::protobuf::FieldDescriptor* GetValueFieldDescriptor( - 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 nullptr; - } - return action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc); -} - -template -void ExecuteActionProto(typename Action::ClassType* module, - const typename Action::Proto& action_proto) { - const auto* field_desc = GetValueFieldDescriptor(action_proto); - if (field_desc == nullptr) return; - 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::ClassType* 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; \ - } - -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 ActionPerformerImpl; // undefined - -template -struct ActionPerformerImpl< - FuzzFunction, void(const MessageProto&), - typename std::enable_if_t>> { - static typename FuzzFunction::ReturnType Invoke( - typename FuzzFunction::ClassType* 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)); - return FuzzFunction::ImplBody(module, arg); - } -}; - -template -struct ActionPerformerImpl>> { - static typename FuzzFunction::ReturnType Invoke( - typename FuzzFunction::ClassType* 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); - return FuzzFunction::ImplBody(module, arg); - } -}; - -template -struct ActionPerformerImpl { - static typename FuzzFunction::ReturnType Invoke(typename FuzzFunction::ClassType* module, - const google::protobuf::Message&, - const google::protobuf::FieldDescriptor*) { - return FuzzFunction::ImplBody(module); - } -}; - -template -struct ActionPerformerImpl { - static typename FuzzFunction::ReturnType Invoke( - typename FuzzFunction::ClassType* 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); - return FuzzFunction::ImplBody(module, arg); - } -}; - -template -struct ActionPerformer : ActionPerformerImpl {}; - -} // 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(Class, Action) \ - class Action { \ - public: \ - using Proto = Action##Proto; \ - using ClassType = Class; \ - 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, void, IFoo* 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, Return, ModuleArg, ...) \ - class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) { \ - public: \ - using ActionType = Action; \ - using ClassType = Action::ClassType; \ - using ReturnType = Return; \ - using Signature = void(__VA_ARGS__); \ - static constexpr const char name[] = #FunctionName; \ - static constexpr const auto tag = \ - Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \ - static ReturnType ImplBody(ModuleArg, ##__VA_ARGS__); \ - \ - private: \ - static bool registered_; \ - }; \ - auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] { \ - auto tag = FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::tag; \ - auto func = &::android::fuzz::ActionPerformer::Invoke; \ - Action::GetFunctionMap()->CheckEmplace(tag, func); \ - return true; \ - })(); \ - Return FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(ModuleArg, ##__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, \ - decltype(std::declval().FunctionName()), \ - Action::ClassType* module) { \ - return module->FunctionName(); \ - } diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index f655522c6..64637c284 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -2901,6 +2901,20 @@ std::ostream& operator<<(std::ostream& os, UpdateState state) { } } +std::ostream& operator<<(std::ostream& os, MergePhase phase) { + switch (phase) { + case MergePhase::NO_MERGE: + return os << "none"; + case MergePhase::FIRST_PHASE: + return os << "first"; + case MergePhase::SECOND_PHASE: + return os << "second"; + default: + LOG(ERROR) << "Unknown merge phase: " << static_cast(phase); + return os << "unknown(" << static_cast(phase) << ")"; + } +} + UpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) { SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock); return status.state(); @@ -3761,7 +3775,7 @@ bool SnapshotManager::Dump(std::ostream& os) { auto update_status = ReadSnapshotUpdateStatus(file.get()); - ss << "Update state: " << ReadUpdateState(file.get()) << std::endl; + ss << "Update state: " << update_status.state() << std::endl; ss << "Using snapuserd: " << update_status.using_snapuserd() << std::endl; ss << "Using userspace snapshots: " << update_status.userspace_snapshots() << std::endl; ss << "Using io_uring: " << update_status.io_uring_enabled() << std::endl; @@ -3776,6 +3790,17 @@ bool SnapshotManager::Dump(std::ostream& os) { << std::endl; ss << "Source build fingerprint: " << update_status.source_build_fingerprint() << std::endl; + if (update_status.state() == UpdateState::Merging) { + ss << "Merge completion: "; + if (!EnsureSnapuserdConnected()) { + ss << "N/A"; + } else { + ss << snapuserd_client_->GetMergePercent() << "%"; + } + ss << std::endl; + ss << "Merge phase: " << update_status.merge_phase() << std::endl; + } + bool ok = true; std::vector snapshots; if (!ListSnapshots(file.get(), &snapshots)) { @@ -3798,6 +3823,7 @@ bool SnapshotManager::Dump(std::ostream& os) { ss << " allocated sectors: " << status.sectors_allocated() << std::endl; ss << " metadata sectors: " << status.metadata_sectors() << std::endl; ss << " compression: " << status.compression_algorithm() << std::endl; + ss << " merge phase: " << DecideMergePhase(status) << std::endl; } os << ss.rdbuf(); return ok; diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp deleted file mode 100644 index aced3edf5..000000000 --- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp +++ /dev/null @@ -1,352 +0,0 @@ -// 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. - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "fuzz_utils.h" -#include "snapshot_fuzz_utils.h" - -using android::base::Error; -using android::base::GetBoolProperty; -using android::base::LogId; -using android::base::LogSeverity; -using android::base::ReadFileToString; -using android::base::Result; -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; -using chromeos_update_engine::DeltaArchiveManifest; -using google::protobuf::FieldDescriptor; -using google::protobuf::Message; -using google::protobuf::RepeatedPtrField; - -// Avoid linking to libgsi since it needs disk I/O. -namespace android::gsi { -bool IsGsiRunning() { - LOG(FATAL) << "Called IsGsiRunning"; - __builtin_unreachable(); -} -std::string GetDsuSlot(const std::string& install_dir) { - LOG(FATAL) << "Called GetDsuSlot(" << install_dir << ")"; - __builtin_unreachable(); -} -} // namespace android::gsi - -namespace android::snapshot { - -const SnapshotFuzzData* current_data = nullptr; -const SnapshotTestModule* current_module = nullptr; - -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); -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, ReturnType, ...) \ - FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, ReturnType, ISnapshotManager* snapshot, \ - ##__VA_ARGS__) - -SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool, bool wipe) { - return snapshot->FinishedSnapshotWrites(wipe); -} - -SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, bool, const ProcessUpdateStateArgs& args) { - std::function before_cancel; - if (args.has_before_cancel()) { - before_cancel = [&]() { return args.fail_before_cancel(); }; - } - return snapshot->ProcessUpdateState({}, before_cancel); -} - -SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, UpdateState, bool has_progress_arg) { - double progress; - return snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr); -} - -SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool, bool has_callback) { - std::function callback; - if (has_callback) { - callback = []() {}; - } - return snapshot->HandleImminentDataWipe(callback); -} - -SNAPSHOT_FUZZ_FUNCTION(Dump, bool) { - std::stringstream ss; - return snapshot->Dump(ss); -} - -SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, bool, const DeltaArchiveManifest& manifest) { - return snapshot->CreateUpdateSnapshots(manifest); -} - -SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, bool, const std::string& name) { - return snapshot->UnmapUpdateSnapshot(name); -} - -SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, bool, - const CreateLogicalAndSnapshotPartitionsArgs& args) { - const std::string* super; - if (args.use_correct_super()) { - super = &GetSnapshotFuzzEnv()->super(); - } else { - super = &args.super(); - } - return snapshot->CreateLogicalAndSnapshotPartitions( - *super, std::chrono::milliseconds(args.timeout_millis())); -} - -SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, CreateResult, - const RecoveryCreateSnapshotDevicesArgs& args) { - std::unique_ptr device; - if (args.has_metadata_device_object()) { - device = std::make_unique(args.metadata_mounted()); - } - return snapshot->RecoveryCreateSnapshotDevices(device); -} - -SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, bool, - 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; - return snapshot->MapUpdateSnapshot(params, &path); -} - -SNAPSHOT_FUZZ_FUNCTION(SwitchSlot, void) { - (void)snapshot; - CHECK(current_module != nullptr); - CHECK(current_module->device_info != nullptr); - current_module->device_info->SwitchSlot(); -} - -// During global init, log all messages to stdio. This is only done once. -int AllowLoggingDuringGlobalInit() { - SetLogger(&StdioLogger); - return 0; -} - -// Only log fatal messages during tests. -void FatalOnlyLogger(LogId logid, LogSeverity severity, const char* tag, const char* file, - 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. -int StopLoggingAfterGlobalInit() { - (void)GetSnapshotFuzzEnv(); - [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silencer; - SetLogger(&FatalOnlyLogger); - return 0; -} - -SnapshotFuzzEnv* GetSnapshotFuzzEnv() { - [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit(); - static SnapshotFuzzEnv env; - return &env; -} - -SnapshotTestModule SetUpTest(const SnapshotFuzzData& snapshot_fuzz_data) { - current_data = &snapshot_fuzz_data; - - auto env = GetSnapshotFuzzEnv(); - env->CheckSoftReset(); - - auto test_module = env->CheckCreateSnapshotManager(snapshot_fuzz_data); - current_module = &test_module; - CHECK(test_module.snapshot); - return test_module; -} - -void TearDownTest() { - current_module = nullptr; - current_data = nullptr; -} - -} // namespace android::snapshot - -DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) { - using namespace android::snapshot; - - [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit(); - auto test_module = SetUpTest(snapshot_fuzz_data); - SnapshotManagerAction::ExecuteAll(test_module.snapshot.get(), snapshot_fuzz_data.actions()); - TearDownTest(); -} - -namespace android::snapshot { - -// Work-around to cast a 'void' value to Result. -template -struct GoodResult { - template - static Result Cast(F&& f) { - return f(); - } -}; - -template <> -struct GoodResult { - template - static Result Cast(F&& f) { - f(); - return {}; - } -}; - -class LibsnapshotFuzzerTest : public ::testing::Test { - protected: - static void SetUpTestCase() { - // Do initialization once. - (void)GetSnapshotFuzzEnv(); - } - void SetUp() override { - bool is_virtual_ab = GetBoolProperty("ro.virtual_ab.enabled", false); - if (!is_virtual_ab) GTEST_SKIP() << "Test only runs on Virtual A/B devices."; - } - void SetUpFuzzData(const std::string& fn) { - auto path = android::base::GetExecutableDirectory() + "/corpus/"s + fn; - std::string proto_text; - ASSERT_TRUE(ReadFileToString(path, &proto_text)); - snapshot_fuzz_data_ = std::make_unique(); - ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(proto_text, - snapshot_fuzz_data_.get())); - test_module_ = android::snapshot::SetUpTest(*snapshot_fuzz_data_); - } - void TearDown() override { android::snapshot::TearDownTest(); } - template - Result Execute(int action_index) { - if (action_index >= snapshot_fuzz_data_->actions_size()) { - return Error() << "Index " << action_index << " is out of bounds (" - << snapshot_fuzz_data_->actions_size() << " actions in corpus"; - } - const auto& action_proto = snapshot_fuzz_data_->actions(action_index); - const auto* field_desc = - android::fuzz::GetValueFieldDescriptor( - action_proto); - if (field_desc == nullptr) { - return Error() << "Action at index " << action_index << " has no value defined."; - } - if (FuzzFunction::tag != field_desc->number()) { - return Error() << "Action at index " << action_index << " is expected to be " - << FuzzFunction::name << ", but it is " << field_desc->name() - << " in corpus."; - } - return GoodResult::Cast([&]() { - return android::fuzz::ActionPerformer::Invoke(test_module_.snapshot.get(), - action_proto, field_desc); - }); - } - - std::unique_ptr snapshot_fuzz_data_; - SnapshotTestModule test_module_; -}; - -#define SNAPSHOT_FUZZ_FN_NAME(name) FUZZ_FUNCTION_CLASS_NAME(SnapshotManagerAction, name) - -MATCHER_P(ResultIs, expected, "") { - if (!arg.ok()) { - *result_listener << arg.error(); - return false; - } - *result_listener << "expected: " << expected; - return arg.value() == expected; -} - -#define ASSERT_RESULT_TRUE(actual) ASSERT_THAT(actual, ResultIs(true)) - -// Check that launch_device.txt is executed correctly. -TEST_F(LibsnapshotFuzzerTest, LaunchDevice) { - SetUpFuzzData("launch_device.txt"); - - int i = 0; - ASSERT_RESULT_TRUE(Execute(i++)); - ASSERT_RESULT_TRUE(Execute(i++)); - ASSERT_RESULT_TRUE(Execute(i++)) << "sys_b"; - ASSERT_RESULT_TRUE(Execute(i++)) << "vnd_b"; - ASSERT_RESULT_TRUE(Execute(i++)) << "prd_b"; - ASSERT_RESULT_TRUE(Execute(i++)); - ASSERT_RESULT_TRUE(Execute(i++)) << "sys_b"; - ASSERT_RESULT_TRUE(Execute(i++)) << "vnd_b"; - ASSERT_RESULT_TRUE(Execute(i++)) << "prd_b"; - ASSERT_RESULT_OK(Execute(i++)); - ASSERT_EQ("_b", test_module_.device_info->GetSlotSuffix()); - ASSERT_RESULT_TRUE(Execute(i++)); - ASSERT_RESULT_TRUE(Execute(i++)); - ASSERT_RESULT_TRUE(Execute(i++)); - ASSERT_RESULT_TRUE(Execute(i++)); - ASSERT_EQ(i, snapshot_fuzz_data_->actions_size()) << "Not all actions are executed."; -} - -} // namespace android::snapshot diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp deleted file mode 100644 index 54c6a0053..000000000 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp +++ /dev/null @@ -1,513 +0,0 @@ -// 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. - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#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::Basename; -using android::base::ReadFileToString; -using android::base::SetProperty; -using android::base::Split; -using android::base::StartsWith; -using android::base::StringPrintf; -using android::base::unique_fd; -using android::base::WriteStringToFile; -using android::dm::DeviceMapper; -using android::dm::DmTarget; -using android::dm::LoopControl; -using android::fiemap::IImageManager; -using android::fiemap::ImageManager; -using android::fs_mgr::BlockDeviceInfo; -using android::fs_mgr::FstabEntry; -using android::fs_mgr::IPartitionOpener; -using chromeos_update_engine::DynamicPartitionMetadata; - -static const char MNT_DIR[] = "/mnt"; -static const char BLOCK_SYSFS[] = "/sys/block"; - -static const char FAKE_ROOT_NAME[] = "snapshot_fuzz"; -static const auto SUPER_IMAGE_SIZE = 16_MiB; -static const auto DATA_IMAGE_SIZE = 16_MiB; -static const auto FAKE_ROOT_SIZE = 64_MiB; - -namespace android::snapshot { - -bool Mkdir(const std::string& path) { - if (mkdir(path.c_str(), 0750) == -1 && errno != EEXIST) { - PLOG(ERROR) << "Cannot create " << path; - return false; - } - return true; -} - -bool RmdirRecursive(const std::string& path) { - auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int { - switch (file_type) { - case FTW_D: - case FTW_DP: - case FTW_DNR: - if (rmdir(child) == -1) { - PLOG(ERROR) << "rmdir " << child; - return -1; - } - return 0; - case FTW_NS: - default: - if (rmdir(child) != -1) break; - [[fallthrough]]; - case FTW_F: - case FTW_SL: - case FTW_SLN: - if (unlink(child) == -1) { - PLOG(ERROR) << "unlink " << child; - return -1; - } - return 0; - } - return 0; - }; - - return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0; -} - -std::string GetLinearBaseDeviceString(const DeviceMapper::TargetInfo& target) { - if (target.spec.target_type != "linear"s) return {}; - auto tokens = Split(target.data, " "); - CHECK_EQ(2, tokens.size()); - return tokens[0]; -} - -std::vector GetSnapshotBaseDeviceStrings(const DeviceMapper::TargetInfo& target) { - if (target.spec.target_type != "snapshot"s && target.spec.target_type != "snapshot-merge"s) - return {}; - auto tokens = Split(target.data, " "); - CHECK_EQ(4, tokens.size()); - return {tokens[0], tokens[1]}; -} - -bool ShouldDeleteLoopDevice(const std::string& node) { - std::string backing_file; - if (ReadFileToString(StringPrintf("%s/loop/backing_file", node.data()), &backing_file)) { - if (StartsWith(backing_file, std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME)) { - return true; - } - } - return false; -} - -std::vector GetTableInfoIfExists(const std::string& dev_name) { - auto& dm = DeviceMapper::Instance(); - std::vector table; - if (!dm.GetTableInfo(dev_name, &table)) { - PCHECK(errno == ENODEV || errno == ENXIO); - return {}; - } - return table; -} - -std::set GetAllBaseDeviceStrings(const std::string& child_dev) { - std::set ret; - for (const auto& child_target : GetTableInfoIfExists(child_dev)) { - auto snapshot_bases = GetSnapshotBaseDeviceStrings(child_target); - ret.insert(snapshot_bases.begin(), snapshot_bases.end()); - - auto linear_base = GetLinearBaseDeviceString(child_target); - if (!linear_base.empty()) { - ret.insert(linear_base); - } - } - return ret; -} - -using PropertyList = std::set; -void InsertProperty(const char* key, const char* /*name*/, void* cookie) { - reinterpret_cast(cookie)->insert(key); -} - -// Attempt to delete all devices that is based on dev_name, including itself. -void CheckDeleteDeviceMapperTree(const std::string& dev_name, bool known_allow_delete = false, - uint64_t depth = 100) { - CHECK(depth > 0) << "Reaching max depth when deleting " << dev_name - << ". There may be devices referencing itself. Check `dmctl list devices -v`."; - - auto& dm = DeviceMapper::Instance(); - auto table = GetTableInfoIfExists(dev_name); - if (table.empty()) { - PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name; - return; - } - - if (!known_allow_delete) { - for (const auto& target : table) { - auto base_device_string = GetLinearBaseDeviceString(target); - if (base_device_string.empty()) continue; - if (ShouldDeleteLoopDevice( - StringPrintf("/sys/dev/block/%s", base_device_string.data()))) { - known_allow_delete = true; - break; - } - } - } - if (!known_allow_delete) { - return; - } - - std::string dev_string; - PCHECK(dm.GetDeviceString(dev_name, &dev_string)); - - std::vector devices; - PCHECK(dm.GetAvailableDevices(&devices)); - for (const auto& child_dev : devices) { - auto child_bases = GetAllBaseDeviceStrings(child_dev.name()); - if (child_bases.find(dev_string) != child_bases.end()) { - CheckDeleteDeviceMapperTree(child_dev.name(), true /* known_allow_delete */, depth - 1); - } - } - - PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name; -} - -// Attempt to clean up residues from previous runs. -void CheckCleanupDeviceMapperDevices() { - auto& dm = DeviceMapper::Instance(); - std::vector devices; - PCHECK(dm.GetAvailableDevices(&devices)); - - for (const auto& dev : devices) { - CheckDeleteDeviceMapperTree(dev.name()); - } -} - -void CheckUmount(const std::string& path) { - PCHECK(TEMP_FAILURE_RETRY(umount(path.data()) == 0) || errno == ENOENT || errno == EINVAL) - << path; -} - -void CheckDetachLoopDevices(const std::set& exclude_names = {}) { - // ~SnapshotFuzzEnv automatically does the following. - std::unique_ptr dir(opendir(BLOCK_SYSFS), closedir); - PCHECK(dir != nullptr) << BLOCK_SYSFS; - LoopControl loop_control; - dirent* dp; - while ((dp = readdir(dir.get())) != nullptr) { - if (exclude_names.find(dp->d_name) != exclude_names.end()) { - continue; - } - if (!ShouldDeleteLoopDevice(StringPrintf("%s/%s", BLOCK_SYSFS, dp->d_name).data())) { - continue; - } - PCHECK(loop_control.Detach(StringPrintf("/dev/block/%s", dp->d_name).data())); - } -} - -void CheckUmountAll() { - CheckUmount(std::string(MNT_DIR) + "/snapshot_fuzz_data"); - CheckUmount(std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME); -} - -class AutoDeleteDir : public AutoDevice { - public: - static std::unique_ptr New(const std::string& path) { - if (!Mkdir(path)) { - return std::unique_ptr(new AutoDeleteDir("")); - } - return std::unique_ptr(new AutoDeleteDir(path)); - } - ~AutoDeleteDir() { - if (!HasDevice()) return; - PCHECK(rmdir(name_.c_str()) == 0 || errno == ENOENT) << name_; - } - - private: - AutoDeleteDir(const std::string& path) : AutoDevice(path) {} -}; - -class AutoUnmount : public AutoDevice { - public: - ~AutoUnmount() { - if (!HasDevice()) return; - CheckUmount(name_); - } - AutoUnmount(const std::string& path) : AutoDevice(path) {} -}; - -class AutoUnmountTmpfs : public AutoUnmount { - public: - static std::unique_ptr New(const std::string& path, uint64_t size) { - if (mount("tmpfs", path.c_str(), "tmpfs", 0, - (void*)StringPrintf("size=%" PRIu64, size).data()) == -1) { - PLOG(ERROR) << "Cannot mount " << path; - return std::unique_ptr(new AutoUnmount("")); - } - return std::unique_ptr(new AutoUnmount(path)); - } - private: - using AutoUnmount::AutoUnmount; -}; - -// A directory on tmpfs. Upon destruct, it is unmounted and deleted. -class AutoMemBasedDir : public AutoDevice { - public: - static std::unique_ptr New(const std::string& name, uint64_t size) { - 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()) { - return std::unique_ptr(new AutoMemBasedDir("")); - } - ret->auto_umount_mount_point_ = AutoUnmountTmpfs::New(ret->mount_path(), size); - if (!ret->auto_umount_mount_point_->HasDevice()) { - return std::unique_ptr(new AutoMemBasedDir("")); - } - // 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 temporary scratch directory. - std::string tmp_path() const { - CHECK(HasDevice()); - 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())); - } - - private: - AutoMemBasedDir(const std::string& name) : AutoDevice(name) {} - std::string mount_path() const { - CHECK(HasDevice()); - return MNT_DIR + "/"s + name_; - } - std::unique_ptr auto_delete_mount_dir_; - std::unique_ptr auto_umount_mount_point_; -}; - -SnapshotFuzzEnv::SnapshotFuzzEnv() { - CheckCleanupDeviceMapperDevices(); - CheckDetachLoopDevices(); - CheckUmountAll(); - - fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE); - CHECK(fake_root_ != nullptr); - CHECK(fake_root_->HasDevice()); - loop_control_ = std::make_unique(); - - fake_data_mount_point_ = MNT_DIR + "/snapshot_fuzz_data"s; - auto_delete_data_mount_point_ = AutoDeleteDir::New(fake_data_mount_point_); - CHECK(auto_delete_data_mount_point_ != nullptr); - CHECK(auto_delete_data_mount_point_->HasDevice()); - - const auto& fake_persist_path = fake_root_->persist_path(); - mapped_super_ = CheckMapImage(fake_persist_path + "/super.img", SUPER_IMAGE_SIZE, - loop_control_.get(), &fake_super_); - mapped_data_ = CheckMapImage(fake_persist_path + "/data.img", DATA_IMAGE_SIZE, - loop_control_.get(), &fake_data_block_device_); - mounted_data_ = CheckMountFormatData(fake_data_block_device_, fake_data_mount_point_); -} - -SnapshotFuzzEnv::~SnapshotFuzzEnv() { - CheckCleanupDeviceMapperDevices(); - mounted_data_ = nullptr; - auto_delete_data_mount_point_ = nullptr; - mapped_data_ = nullptr; - mapped_super_ = nullptr; - CheckDetachLoopDevices(); - loop_control_ = nullptr; - fake_root_ = nullptr; - CheckUmountAll(); -} - -void CheckZeroFill(const std::string& file, size_t size) { - std::string zeros(size, '\0'); - PCHECK(WriteStringToFile(zeros, file)) << "Cannot write zeros to " << file; -} - -void SnapshotFuzzEnv::CheckSoftReset() { - fake_root_->CheckSoftReset(); - CheckZeroFill(super(), SUPER_IMAGE_SIZE); - CheckCleanupDeviceMapperDevices(); - CheckDetachLoopDevices({Basename(fake_super_), Basename(fake_data_block_device_)}); -} - -std::unique_ptr SnapshotFuzzEnv::CheckCreateFakeImageManager() { - auto metadata_dir = fake_root_->tmp_path() + "/images_manager_metadata"; - auto data_dir = fake_data_mount_point_ + "/image_manager_data"; - PCHECK(Mkdir(metadata_dir)); - PCHECK(Mkdir(data_dir)); - return SnapshotFuzzImageManager::Open(metadata_dir, data_dir); -} - -// 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; -} - -class AutoDetachLoopDevice : public AutoDevice { - public: - AutoDetachLoopDevice(LoopControl* control, const std::string& device) - : AutoDevice(device), control_(control) {} - ~AutoDetachLoopDevice() { PCHECK(control_->Detach(name_)) << name_; } - - private: - LoopControl* control_; -}; - -std::unique_ptr SnapshotFuzzEnv::CheckMapImage(const std::string& img_path, - uint64_t size, LoopControl* control, - std::string* mapped_path) { - CheckZeroFill(img_path, size); - CheckCreateLoopDevice(control, img_path, 1s, mapped_path); - - return std::make_unique(control, *mapped_path); -} - -SnapshotTestModule SnapshotFuzzEnv::CheckCreateSnapshotManager(const SnapshotFuzzData& data) { - SnapshotTestModule ret; - auto partition_opener = std::make_unique(super()); - ret.opener = partition_opener.get(); - CheckWriteSuperMetadata(data, *partition_opener); - auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata"; - PCHECK(Mkdir(metadata_dir)); - if (data.has_metadata_snapshots_dir()) { - PCHECK(Mkdir(metadata_dir + "/snapshots")); - } - - ret.device_info = new SnapshotFuzzDeviceInfo(this, data.device_info_data(), - std::move(partition_opener), metadata_dir); - auto snapshot = SnapshotManager::New(ret.device_info /* takes ownership */); - ret.snapshot = std::move(snapshot); - - return ret; -} - -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())); -} - -std::unique_ptr SnapshotFuzzEnv::CheckMountFormatData(const std::string& blk_device, - const std::string& mount_point) { - FstabEntry entry{ - .blk_device = blk_device, - .length = static_cast(DATA_IMAGE_SIZE), - .fs_type = "ext4", - .mount_point = mount_point, - }; - CHECK(0 == fs_mgr_do_format(entry)); - CHECK(0 == fs_mgr_do_mount_one(entry)); - return std::make_unique(mount_point); -} - -SnapshotFuzzImageManager::~SnapshotFuzzImageManager() { - // Remove relevant gsid.mapped_images.* props. - for (const auto& name : mapped_) { - CHECK(UnmapImageIfExists(name)) << "Cannot unmap " << name; - } -} - -bool SnapshotFuzzImageManager::MapImageDevice(const std::string& name, - const std::chrono::milliseconds& timeout_ms, - std::string* path) { - if (impl_->MapImageDevice(name, timeout_ms, path)) { - mapped_.insert(name); - return true; - } - return false; -} - -} // namespace android::snapshot diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h deleted file mode 100644 index eb8246aba..000000000 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h +++ /dev/null @@ -1,214 +0,0 @@ -// 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. - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -// libsnapshot-specific code for fuzzing. Defines fake classes that are depended -// by SnapshotManager. - -#include "android/snapshot/snapshot_fuzz.pb.h" -#include "libsnapshot/snapshot.h" - -namespace android::snapshot { - -class AutoMemBasedDir; -class SnapshotFuzzDeviceInfo; - -class NoOpAutoDevice : public AutoDevice { - public: - NoOpAutoDevice(bool mounted) : AutoDevice(mounted ? "no_op" : "") {} -}; - -struct SnapshotTestModule { - std::unique_ptr snapshot; - SnapshotFuzzDeviceInfo* device_info = nullptr; - TestPartitionOpener* opener = nullptr; -}; - -// Prepare test environment. This has a heavy overhead and should be done once. -class SnapshotFuzzEnv { - public: - // Check if test should run at all. - static bool ShouldSkipTest(); - - // Initialize the environment. - SnapshotFuzzEnv(); - ~SnapshotFuzzEnv(); - - // Soft reset part of the environment before running the next test. - // 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. - SnapshotTestModule CheckCreateSnapshotManager(const SnapshotFuzzData& data); - - std::unique_ptr CheckCreateFakeImageManager(); - - // Return path to super partition. - const std::string& super() const; - - private: - std::unique_ptr fake_root_; - std::unique_ptr loop_control_; - std::string fake_data_mount_point_; - std::unique_ptr auto_delete_data_mount_point_; - std::unique_ptr mapped_super_; - std::string fake_super_; - std::unique_ptr mapped_data_; - std::string fake_data_block_device_; - std::unique_ptr mounted_data_; - - static std::unique_ptr CheckMapImage(const std::string& fake_persist_path, - uint64_t size, - android::dm::LoopControl* control, - std::string* mapped_path); - static std::unique_ptr CheckMountFormatData(const std::string& blk_device, - const std::string& mount_point); - - void CheckWriteSuperMetadata(const SnapshotFuzzData& proto, - const android::fs_mgr::IPartitionOpener& opener); -}; - -class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo { - public: - using MergeStatus = ISnapshotManager::IDeviceInfo::MergeStatus; - // Client is responsible for maintaining the lifetime of |data|. - SnapshotFuzzDeviceInfo(SnapshotFuzzEnv* env, const FuzzDeviceInfoData& data, - std::unique_ptr&& partition_opener, - const std::string& metadata_dir) - : env_(env), - data_(&data), - partition_opener_(std::move(partition_opener)), - metadata_dir_(metadata_dir), - dm_(android::dm::DeviceMapper::Instance()) {} - - // Following APIs are mocked. - std::string GetMetadataDir() const override { return metadata_dir_; } - std::string GetSuperDevice(uint32_t) const override { - // TestPartitionOpener can recognize this. - return "super"; - } - const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override { - return *partition_opener_; - } - - // Following APIs are fuzzed. - std::string GetSlotSuffix() const override { return CurrentSlotIsA() ? "_a" : "_b"; } - std::string GetOtherSlotSuffix() const override { return CurrentSlotIsA() ? "_b" : "_a"; } - bool IsOverlayfsSetup() const override { return data_->is_overlayfs_setup(); } - bool SetBootControlMergeStatus(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(); } - bool IsFirstStageInit() const override { return false; } - android::dm::IDeviceMapper& GetDeviceMapper() override { return dm_; } - std::unique_ptr OpenImageManager() const { - return env_->CheckCreateFakeImageManager(); - } - - void SwitchSlot() { switched_slot_ = !switched_slot_; } - - private: - SnapshotFuzzEnv* env_; - const FuzzDeviceInfoData* data_; - std::unique_ptr partition_opener_; - std::string metadata_dir_; - bool switched_slot_ = false; - android::dm::DeviceMapper& dm_; - - bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; } -}; - -// A spy class on ImageManager implementation. Upon destruction, unmaps all images -// map through this object. -class SnapshotFuzzImageManager : public android::fiemap::IImageManager { - public: - static std::unique_ptr Open(const std::string& metadata_dir, - const std::string& data_dir) { - auto impl = android::fiemap::ImageManager::Open(metadata_dir, data_dir); - if (impl == nullptr) return nullptr; - return std::unique_ptr( - new SnapshotFuzzImageManager(std::move(impl))); - } - - ~SnapshotFuzzImageManager(); - - // Spied APIs. - bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms, - std::string* path) override; - - // Other functions call through. - android::fiemap::FiemapStatus CreateBackingImage( - const std::string& name, uint64_t size, int flags, - std::function&& on_progress) override { - return impl_->CreateBackingImage(name, size, flags, std::move(on_progress)); - } - bool DeleteBackingImage(const std::string& name) override { - return impl_->DeleteBackingImage(name); - } - bool UnmapImageDevice(const std::string& name) override { - return impl_->UnmapImageDevice(name); - } - bool BackingImageExists(const std::string& name) override { - return impl_->BackingImageExists(name); - } - bool IsImageMapped(const std::string& name) override { return impl_->IsImageMapped(name); } - bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name, - std::string* dev) override { - return impl_->MapImageWithDeviceMapper(opener, name, dev); - } - bool GetMappedImageDevice(const std::string& name, std::string* device) override { - return impl_->GetMappedImageDevice(name, device); - } - bool MapAllImages(const std::function)>& init) override { - return impl_->MapAllImages(init); - } - bool DisableImage(const std::string& name) override { return impl_->DisableImage(name); } - bool RemoveDisabledImages() override { return impl_->RemoveDisabledImages(); } - std::vector GetAllBackingImages() override { return impl_->GetAllBackingImages(); } - android::fiemap::FiemapStatus ZeroFillNewImage(const std::string& name, - uint64_t bytes) override { - return impl_->ZeroFillNewImage(name, bytes); - } - bool RemoveAllImages() override { return impl_->RemoveAllImages(); } - bool UnmapImageIfExists(const std::string& name) override { - return impl_->UnmapImageIfExists(name); - } - bool IsImageDisabled(const std::string& name) override { return impl_->IsImageDisabled(name); } - - private: - std::unique_ptr impl_; - std::set mapped_; - - SnapshotFuzzImageManager(std::unique_ptr&& impl) - : impl_(std::move(impl)) {} -}; - -} // namespace android::snapshot