Merge changes from topic "snapshot_fuzz"

* changes:
  libsnapshot_fuzzer: Add tests
  libsnapshot_fuzzer: add initial corpus
  libsnapshot_fuzzer: Attempt to cleanup env before and after
  libsnapshot_fuzzer: add new test directive to switch slot
  libsnapshot_fuzzer: mount data image
  libsnapshot_fuzzer: also create snapshots dir
  libsnapshot_fuzzer: Add ZERO to operation types
This commit is contained in:
Yifan Hong 2020-05-14 20:41:08 +00:00 committed by Gerrit Code Review
commit fa8bf5be4e
10 changed files with 706 additions and 141 deletions

View File

@ -14,6 +14,9 @@
},
{
"name": "vts_libsnapshot_test"
},
{
"name": "libsnapshot_fuzzer_test"
}
]
}

View File

@ -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,
}

View File

@ -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;
}

View File

@ -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 {
}
}

View File

@ -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
}

View File

@ -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 <typename Action>
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 <typename Action>
void ExecuteActionProto(typename Action::ClassType* module,
const typename Action::Proto& action_proto) {
const auto* field_desc = GetValueFieldDescriptor<Action>(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 <typename Action>
void ExecuteAllActionProtos(
typename Action::Class* module,
typename Action::ClassType* module,
const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
for (const auto& proto : action_protos) {
ExecuteActionProto<Action>(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 <typename FuzzFunction, typename Signature, typename Enabled = void>
struct ActionPerfomer; // undefined
struct ActionPerformerImpl; // undefined
template <typename FuzzFunction, typename MessageProto>
struct ActionPerfomer<
struct ActionPerformerImpl<
FuzzFunction, void(const MessageProto&),
typename std::enable_if_t<std::is_base_of_v<google::protobuf::Message, MessageProto>>> {
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<std::remove_reference_t<MessageProto>>(
action_proto.GetReflection()->GetMessage(action_proto, field_desc));
FuzzFunction::ImplBody(module, arg);
return FuzzFunction::ImplBody(module, arg);
}
};
template <typename FuzzFunction, typename Primitive>
struct ActionPerfomer<FuzzFunction, void(Primitive),
typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
static void Invoke(typename FuzzFunction::Class* module,
const google::protobuf::Message& action_proto,
const google::protobuf::FieldDescriptor* field_desc) {
struct ActionPerformerImpl<FuzzFunction, void(Primitive),
typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
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<Primitive>::fp, action_proto.GetReflection(),
action_proto, field_desc);
FuzzFunction::ImplBody(module, arg);
return FuzzFunction::ImplBody(module, arg);
}
};
template <typename FuzzFunction>
struct ActionPerfomer<FuzzFunction, void()> {
static void Invoke(typename FuzzFunction::Class* module, const google::protobuf::Message&,
const google::protobuf::FieldDescriptor*) {
FuzzFunction::ImplBody(module);
struct ActionPerformerImpl<FuzzFunction, void()> {
static typename FuzzFunction::ReturnType Invoke(typename FuzzFunction::ClassType* module,
const google::protobuf::Message&,
const google::protobuf::FieldDescriptor*) {
return FuzzFunction::ImplBody(module);
}
};
template <typename FuzzFunction>
struct ActionPerfomer<FuzzFunction, void(const std::string&)> {
static void Invoke(typename FuzzFunction::Class* module,
const google::protobuf::Message& action_proto,
const google::protobuf::FieldDescriptor* field_desc) {
struct ActionPerformerImpl<FuzzFunction, void(const std::string&)> {
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 <typename FuzzFunction>
struct ActionPerformer : ActionPerformerImpl<FuzzFunction, typename FuzzFunction::Signature> {};
} // namespace android::fuzz
// Fuzz existing C++ class, ClassType, with a collection of functions under the name Action.
@ -197,11 +209,11 @@ struct ActionPerfomer<FuzzFunction, void(const std::string&)> {
// 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<Class>; \
static FunctionMap* GetFunctionMap() { \
static Action::FunctionMap map; \
@ -225,29 +237,33 @@ struct ActionPerfomer<FuzzFunction, void(const std::string&)> {
// }
// 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<FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName), \
void(__VA_ARGS__)>::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<FUZZ_FUNCTION_CLASS_NAME( \
Action, FunctionName)>::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<FuzzFunction, void(const std::string&)> {
// 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<Action::ClassType>().FunctionName()), \
Action::ClassType* module) { \
return module->FunctionName(); \
}

View File

@ -21,14 +21,21 @@
#include <tuple>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/result.h>
#include <gtest/gtest.h>
#include <src/libfuzzer/libfuzzer_macro.h>
#include <storage_literals/storage_literals.h>
#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<bool()> 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<void()> 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<AutoDevice> device;
if (args.has_metadata_device_object()) {
device = std::make_unique<DummyAutoDevice>(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<TestPartitionOpener>(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<void>.
template <typename T>
struct GoodResult {
template <typename F>
static Result<T> Cast(F&& f) {
return f();
}
};
template <>
struct GoodResult<void> {
template <typename F>
static Result<void> 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<SnapshotFuzzData>();
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 <typename FuzzFunction>
Result<typename FuzzFunction::ReturnType> 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<typename FuzzFunction::ActionType>(
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<typename FuzzFunction::ReturnType>::Cast([&]() {
return android::fuzz::ActionPerformer<FuzzFunction>::Invoke(test_module_.snapshot.get(),
action_proto, field_desc);
});
}
std::unique_ptr<SnapshotFuzzData> 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<SNAPSHOT_FUZZ_FN_NAME(BeginUpdate)>(i++));
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateUpdateSnapshots)>(i++));
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "sys_b";
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "vnd_b";
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "prd_b";
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(FinishedSnapshotWrites)>(i++));
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "sys_b";
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "vnd_b";
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "prd_b";
ASSERT_RESULT_OK(Execute<SNAPSHOT_FUZZ_FN_NAME(SwitchSlot)>(i++));
ASSERT_EQ("_b", test_module_.device_info->GetSlotSuffix());
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(NeedSnapshotsInFirstStageMount)>(i++));
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateLogicalAndSnapshotPartitions)>(i++));
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(InitiateMerge)>(i++));
ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(ProcessUpdateState)>(i++));
ASSERT_EQ(i, snapshot_fuzz_data_->actions_size()) << "Not all actions are executed.";
}
} // namespace android::snapshot

View File

@ -24,7 +24,11 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/properties.h>
#include <fs_mgr.h>
#include <libsnapshot/auto_device.h>
#include <libsnapshot/snapshot.h>
#include <storage_literals/storage_literals.h>
@ -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<std::string> 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<DeviceMapper::TargetInfo> GetTableInfoIfExists(const std::string& dev_name) {
auto& dm = DeviceMapper::Instance();
std::vector<DeviceMapper::TargetInfo> table;
if (!dm.GetTableInfo(dev_name, &table)) {
PCHECK(errno == ENODEV);
return {};
}
return table;
}
std::set<std::string> GetAllBaseDeviceStrings(const std::string& child_dev) {
std::set<std::string> 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<std::string>;
void InsertProperty(const char* key, const char* /*name*/, void* cookie) {
reinterpret_cast<PropertyList*>(cookie)->insert(key);
}
void CheckUnsetGsidProps() {
PropertyList list;
property_list(&InsertProperty, reinterpret_cast<void*>(&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<DeviceMapper::DmBlockDevice> 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<DeviceMapper::DmBlockDevice> 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<std::string>& exclude_names = {}) {
// ~SnapshotFuzzEnv automatically does the following.
std::unique_ptr<DIR, decltype(&closedir)> 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<AutoDeleteDir> 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<AutoUnmount> 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<AutoUnmount>(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<AutoMemBasedDir> New(const std::string& name, uint64_t size) {
if (!Mkdir(MNT_DIR)) {
return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
}
auto ret = std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(name));
ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path());
if (!ret->auto_delete_mount_dir_->HasDevice()) {
return std::unique_ptr<AutoMemBasedDir>(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<AutoMemBasedDir>(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<LoopControl>();
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<IImageManager> 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<AutoDevice> 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<AutoDevice> 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<AutoDetachLoopDevice>(control, *fake_super);
return std::make_unique<AutoDetachLoopDevice>(control, *mapped_path);
}
std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CheckCreateSnapshotManager(
const SnapshotFuzzData& data) {
SnapshotTestModule SnapshotFuzzEnv::CheckCreateSnapshotManager(const SnapshotFuzzData& data) {
SnapshotTestModule ret;
auto partition_opener = std::make_unique<TestPartitionOpener>(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<AutoDevice> SnapshotFuzzEnv::CheckMountFormatData(const std::string& blk_device,
const std::string& mount_point) {
FstabEntry entry{
.blk_device = blk_device,
.length = static_cast<off64_t>(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<AutoUnmount>(mount_point);
}
} // namespace android::snapshot

View File

@ -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<ISnapshotManager> 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<ISnapshotManager> 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<AutoMemBasedDir> fake_root_;
std::unique_ptr<android::dm::LoopControl> loop_control_;
std::string fake_data_mount_point_;
std::unique_ptr<AutoDevice> auto_delete_data_mount_point_;
std::unique_ptr<AutoDevice> mapped_super_;
std::string fake_super_;
std::unique_ptr<AutoDevice> mapped_data_;
std::string fake_data_block_device_;
std::unique_ptr<AutoDevice> mounted_data_;
static std::unique_ptr<android::fiemap::IImageManager> CheckCreateFakeImageManager(
const std::string& fake_tmp_path);
static std::unique_ptr<AutoDevice> CheckMapSuper(const std::string& fake_persist_path,
const std::string& metadata_dir, const std::string& data_dir);
static std::unique_ptr<AutoDevice> 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<AutoDevice> 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<TestPartitionOpener> partition_opener_;
std::string metadata_dir_;
bool switched_slot_ = false;
bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; }
};
} // namespace android::snapshot

View File

@ -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;