diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING index 676f446e7..6cd043013 100644 --- a/fs_mgr/TEST_MAPPING +++ b/fs_mgr/TEST_MAPPING @@ -14,6 +14,9 @@ }, { "name": "vts_libsnapshot_test" + }, + { + "name": "libsnapshot_fuzzer_test" } ] } diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index e916693ea..2783e4de4 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -246,8 +246,8 @@ cc_test { gtest: false, } -cc_fuzz { - name: "libsnapshot_fuzzer", +cc_defaults { + name: "libsnapshot_fuzzer_defaults", // TODO(b/154633114): make host supported. // host_supported: true, @@ -289,7 +289,12 @@ cc_fuzz { 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, @@ -298,3 +303,14 @@ cc_fuzz { 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, +} diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto index 91fbb60eb..a55b42acb 100644 --- a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto +++ b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto @@ -64,6 +64,7 @@ message SnapshotManagerActionProto { bool has_metadata_device_object = 1; bool metadata_mounted = 2; } + reserved 18 to 9999; oneof value { NoArgs begin_update = 1; NoArgs cancel_update = 2; @@ -82,6 +83,9 @@ message SnapshotManagerActionProto { 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; } } @@ -97,7 +101,10 @@ message SnapshotFuzzData { 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 5 to 9999; + reserved 6 to 9999; repeated SnapshotManagerActionProto actions = 10000; } diff --git a/fs_mgr/libsnapshot/corpus/launch_device.txt b/fs_mgr/libsnapshot/corpus/launch_device.txt new file mode 100644 index 000000000..55a7f2ccd --- /dev/null +++ b/fs_mgr/libsnapshot/corpus/launch_device.txt @@ -0,0 +1,161 @@ +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 index 29101298e..0e5767422 100755 --- a/fs_mgr/libsnapshot/fuzz.sh +++ b/fs_mgr/libsnapshot/fuzz.sh @@ -3,7 +3,8 @@ 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_CORPSE_DIR=/data/local/tmp/${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 @@ -26,13 +27,14 @@ build_cov() { prepare_device() { adb root && adb remount && - adb shell mkdir -p ${DEVICE_CORPSE_DIR} && + 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}/${FUZZ_BINARY} ${FUZZ_BINARY} && + adb push ${ANDROID_PRODUCT_OUT}/${DEVICE_INIT_CORPUS_DIR} $(dirname ${FUZZ_BINARY}) } prepare_host() { @@ -52,7 +54,7 @@ generate_corpus() { prepare_device && build_normal && push_binary && - adb shell ${FUZZ_BINARY} "$@" ${DEVICE_CORPSE_DIR} + adb shell ${FUZZ_BINARY} "$@" ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR} } run_snapshot_fuzz() { @@ -62,7 +64,7 @@ run_snapshot_fuzz() { adb shell GCOV_PREFIX=${DEVICE_GCOV_DIR} GCOV_PREFIX_STRIP=3 \ ${FUZZ_BINARY} \ -runs=0 \ - ${DEVICE_CORPSE_DIR} + ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR} } show_fuzz_result() { @@ -82,7 +84,7 @@ exec llvm-cov gcov "$@" # run_snapshot_fuzz -runs=10000 run_snapshot_fuzz_all() { - generate_corpse "$@" && + generate_corpus "$@" && run_snapshot_fuzz && show_fuzz_result } diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h index 4dc6cdc0b..20b13b2fa 100644 --- a/fs_mgr/libsnapshot/fuzz_utils.h +++ b/fs_mgr/libsnapshot/fuzz_utils.h @@ -68,17 +68,25 @@ int CheckConsistency() { return 0; } +// Get the field descriptor for the oneof field in the action message. If no oneof field is set, +// return nullptr. template -void ExecuteActionProto(typename Action::Class* module, - const typename Action::Proto& action_proto) { +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; + return nullptr; } + return action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc); +} - const auto* field_desc = 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); @@ -89,7 +97,7 @@ void ExecuteActionProto(typename Action::Class* module, template void ExecuteAllActionProtos( - typename Action::Class* module, + typename Action::ClassType* module, const google::protobuf::RepeatedPtrField& action_protos) { for (const auto& proto : action_protos) { ExecuteActionProto(module, proto); @@ -134,53 +142,57 @@ FUZZ_DEFINE_PRIMITIVE_GETTER(float, GetFloat); // ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction // with these arguments. template -struct ActionPerfomer; // undefined +struct ActionPerformerImpl; // undefined template -struct ActionPerfomer< +struct ActionPerformerImpl< 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) { + 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)); - FuzzFunction::ImplBody(module, arg); + return 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) { +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); - FuzzFunction::ImplBody(module, arg); + return 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); +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 ActionPerfomer { - static void Invoke(typename FuzzFunction::Class* module, - const google::protobuf::Message& action_proto, - const google::protobuf::FieldDescriptor* field_desc) { +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); - FuzzFunction::ImplBody(module, arg); + 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. @@ -197,11 +209,11 @@ struct ActionPerfomer { // 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) \ +#define FUZZ_CLASS(Class, Action) \ class Action { \ public: \ using Proto = Action##Proto; \ - using Class = ClassType; \ + using ClassType = Class; \ using FunctionMap = android::fuzz::FunctionMap; \ static FunctionMap* GetFunctionMap() { \ static Action::FunctionMap map; \ @@ -225,29 +237,33 @@ struct ActionPerfomer { // } // class Foo { public: void DoAwesomeFoo(bool arg); }; // FUZZ_OBJECT(FooAction, Foo); -// FUZZ_FUNCTION(FooAction, DoFoo, module, bool arg) { +// 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, 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__) +#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 { @@ -261,5 +277,9 @@ struct ActionPerfomer { // 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(); } +#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_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp index 421154d3c..5b145c31f 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp +++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp @@ -21,14 +21,21 @@ #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; @@ -37,6 +44,8 @@ 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. @@ -54,6 +63,7 @@ std::string GetDsuSlot(const std::string& install_dir) { namespace android::snapshot { const SnapshotFuzzData* current_data = nullptr; +const SnapshotTestModule* current_module = nullptr; SnapshotFuzzEnv* GetSnapshotFuzzEnv(); @@ -73,48 +83,49 @@ 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__) +#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ReturnType, ...) \ + FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, ReturnType, ISnapshotManager* snapshot, \ + ##__VA_ARGS__) -SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool wipe) { - (void)snapshot->FinishedSnapshotWrites(wipe); +SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool, bool wipe) { + return snapshot->FinishedSnapshotWrites(wipe); } -SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, const ProcessUpdateStateArgs& args) { +SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, bool, const ProcessUpdateStateArgs& args) { std::function before_cancel; if (args.has_before_cancel()) { before_cancel = [&]() { return args.fail_before_cancel(); }; } - (void)snapshot->ProcessUpdateState({}, before_cancel); + return snapshot->ProcessUpdateState({}, before_cancel); } -SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, bool has_progress_arg) { +SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, UpdateState, bool has_progress_arg) { double progress; - (void)snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr); + return snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr); } -SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool has_callback) { +SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool, bool has_callback) { std::function callback; if (has_callback) { callback = []() {}; } - (void)snapshot->HandleImminentDataWipe(callback); + return snapshot->HandleImminentDataWipe(callback); } -SNAPSHOT_FUZZ_FUNCTION(Dump) { +SNAPSHOT_FUZZ_FUNCTION(Dump, bool) { std::stringstream ss; - (void)snapshot->Dump(ss); + return snapshot->Dump(ss); } -SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, const DeltaArchiveManifest& manifest) { - (void)snapshot->CreateUpdateSnapshots(manifest); +SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, bool, const DeltaArchiveManifest& manifest) { + return snapshot->CreateUpdateSnapshots(manifest); } -SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, const std::string& name) { - (void)snapshot->UnmapUpdateSnapshot(name); +SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, bool, const std::string& name) { + return snapshot->UnmapUpdateSnapshot(name); } -SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, +SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, bool, const CreateLogicalAndSnapshotPartitionsArgs& args) { const std::string* super; if (args.use_correct_super()) { @@ -122,20 +133,21 @@ SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, } else { super = &args.super(); } - (void)snapshot->CreateLogicalAndSnapshotPartitions( + return snapshot->CreateLogicalAndSnapshotPartitions( *super, std::chrono::milliseconds(args.timeout_millis())); } -SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, +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()); } - (void)snapshot->RecoveryCreateSnapshotDevices(device); + return snapshot->RecoveryCreateSnapshotDevices(device); } -SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, const CreateLogicalPartitionParamsProto& params_proto) { +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()) { @@ -152,7 +164,14 @@ SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, const CreateLogicalPartitionParamsProt params.device_name = params_proto.device_name(); params.partition_opener = partition_opener.get(); std::string path; - (void)snapshot->MapUpdateSnapshot(params, &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. @@ -186,7 +205,8 @@ 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; + (void)GetSnapshotFuzzEnv(); + [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silencer; SetLogger(&FatalOnlyLogger); return 0; } @@ -194,22 +214,139 @@ int StopLoggingAfterGlobalInit() { SnapshotFuzzEnv* GetSnapshotFuzzEnv() { [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit(); static SnapshotFuzzEnv env; - [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit(); 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; - current_data = &snapshot_fuzz_data; - - auto env = GetSnapshotFuzzEnv(); - env->CheckSoftReset(); - - auto snapshot_manager = env->CheckCreateSnapshotManager(snapshot_fuzz_data); - CHECK(snapshot_manager); - - SnapshotManagerAction::ExecuteAll(snapshot_manager.get(), snapshot_fuzz_data.actions()); + [[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 index 8101d03b7..c9f1ab02b 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp +++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp @@ -24,7 +24,11 @@ #include #include +#include #include +#include +#include +#include #include #include #include @@ -41,21 +45,30 @@ 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; -// This directory is exempted from pinning in ImageManager. -static const char MNT_DIR[] = "/data/gsi/ota/test/"; +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 { @@ -98,6 +111,149 @@ bool RmdirRecursive(const std::string& path) { 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); + 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); +} + +void CheckUnsetGsidProps() { + PropertyList list; + property_list(&InsertProperty, reinterpret_cast(&list)); + for (const auto& key : list) { + SetProperty(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) { @@ -108,9 +264,7 @@ class AutoDeleteDir : public AutoDevice { } ~AutoDeleteDir() { if (!HasDevice()) return; - if (rmdir(name_.c_str()) == -1) { - PLOG(ERROR) << "Cannot remove " << name_; - } + PCHECK(rmdir(name_.c_str()) == 0 || errno == ENOENT) << name_; } private: @@ -118,6 +272,15 @@ class AutoDeleteDir : public AutoDevice { }; 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, @@ -127,30 +290,20 @@ class AutoUnmount : public AutoDevice { } return std::unique_ptr(new AutoUnmount(path)); } - ~AutoUnmount() { - if (!HasDevice()) return; - if (umount(name_.c_str()) == -1) { - PLOG(ERROR) << "Cannot umount " << name_; - } - } - private: - AutoUnmount(const std::string& path) : AutoDevice(path) {} + 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) { - 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()) { return std::unique_ptr(new AutoMemBasedDir("")); } - ret->auto_umount_mount_point_ = AutoUnmount::New(ret->mount_path(), size); + ret->auto_umount_mount_point_ = AutoUnmountTmpfs::New(ret->mount_path(), size); if (!ret->auto_umount_mount_point_->HasDevice()) { return std::unique_ptr(new AutoMemBasedDir("")); } @@ -191,14 +344,41 @@ class AutoMemBasedDir : public AutoDevice { }; SnapshotFuzzEnv::SnapshotFuzzEnv() { + CheckUnsetGsidProps(); + 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(); - mapped_super_ = CheckMapSuper(fake_root_->persist_path(), loop_control_.get(), &fake_super_); + + 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() = default; +SnapshotFuzzEnv::~SnapshotFuzzEnv() { + CheckUnsetGsidProps(); + 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'); @@ -208,15 +388,12 @@ void CheckZeroFill(const std::string& file, size_t size) { 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( - const std::string& path) { - auto images_dir = path + "/images"; - auto metadata_dir = images_dir + "/metadata"; - auto data_dir = images_dir + "/data"; - - PCHECK(Mkdir(images_dir)); + const std::string& metadata_dir, const std::string& data_dir) { PCHECK(Mkdir(metadata_dir)); PCHECK(Mkdir(data_dir)); return ImageManager::Open(metadata_dir, data_dir); @@ -236,36 +413,42 @@ class AutoDetachLoopDevice : public AutoDevice { public: AutoDetachLoopDevice(LoopControl* control, const std::string& device) : AutoDevice(device), control_(control) {} - ~AutoDetachLoopDevice() { control_->Detach(name_); } + ~AutoDetachLoopDevice() { PCHECK(control_->Detach(name_)) << 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); +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, *fake_super); + return std::make_unique(control, *mapped_path); } -std::unique_ptr SnapshotFuzzEnv::CheckCreateSnapshotManager( - const SnapshotFuzzData& data) { +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")); + } - 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()); + ret.device_info = new SnapshotFuzzDeviceInfo(data.device_info_data(), + std::move(partition_opener), metadata_dir); + auto snapshot = SnapshotManager::New(ret.device_info /* takes ownership */); + snapshot->images_ = + CheckCreateFakeImageManager(fake_root_->tmp_path() + "/images_manager_metadata", + fake_data_mount_point_ + "/image_manager_data"); snapshot->has_local_image_manager_ = data.manager_data().is_local_image_manager(); + ret.snapshot = std::move(snapshot); - return snapshot; + return ret; } const std::string& SnapshotFuzzEnv::super() const { @@ -311,4 +494,17 @@ void SnapshotFuzzEnv::CheckWriteSuperMetadata(const SnapshotFuzzData& data, 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, false /* crypt_footer */)); + CHECK(0 == fs_mgr_do_mount_one(entry)); + return std::make_unique(mount_point); +} + } // namespace android::snapshot diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h index 5533defa7..240508881 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h +++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h @@ -31,12 +31,19 @@ namespace android::snapshot { class AutoMemBasedDir; +class SnapshotFuzzDeviceInfo; class DummyAutoDevice : public AutoDevice { public: DummyAutoDevice(bool mounted) : AutoDevice(mounted ? "dummy" : "") {} }; +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: @@ -54,7 +61,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 SnapshotFuzzData& data); + SnapshotTestModule CheckCreateSnapshotManager(const SnapshotFuzzData& data); // Return path to super partition. const std::string& super() const; @@ -62,14 +69,22 @@ class SnapshotFuzzEnv { 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 CheckCreateFakeImageManager( - const std::string& fake_tmp_path); - static std::unique_ptr CheckMapSuper(const std::string& fake_persist_path, + const std::string& metadata_dir, const std::string& data_dir); + static std::unique_ptr CheckMapImage(const std::string& fake_persist_path, + uint64_t size, android::dm::LoopControl* control, - std::string* fake_super); + 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); @@ -97,10 +112,8 @@ 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"; - } + 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(android::hardware::boot::V1_1::MergeStatus) override { return data_->allow_set_boot_control_merge_status(); @@ -110,10 +123,15 @@ class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo { } bool IsRecovery() const override { return data_->is_recovery(); } + void SwitchSlot() { switched_slot_ = !switched_slot_; } + private: const FuzzDeviceInfoData* data_; std::unique_ptr partition_opener_; std::string metadata_dir_; + bool switched_slot_ = false; + + bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; } }; } // namespace android::snapshot diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto index be5e1fe69..8a11eaa95 100644 --- a/fs_mgr/libsnapshot/update_engine/update_metadata.proto +++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto @@ -45,7 +45,12 @@ message PartitionInfo { } message InstallOperation { - enum Type { SOURCE_COPY = 4; } + enum Type { + SOURCE_COPY = 4; + // Not used by libsnapshot. Declared here so that the fuzzer has an + // alternative value to use for |type|. + ZERO = 6; + } required Type type = 1; repeated Extent src_extents = 4; repeated Extent dst_extents = 6;