1 // Copyright (C) 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <stddef.h>
16 #include <stdint.h>
17 #include <sysexits.h>
18
19 #include <functional>
20 #include <sstream>
21 #include <tuple>
22
23 #include <android-base/logging.h>
24 #include <android-base/properties.h>
25 #include <android-base/result.h>
26 #include <gtest/gtest.h>
27 #include <src/libfuzzer/libfuzzer_macro.h>
28 #include <storage_literals/storage_literals.h>
29
30 #include "fuzz_utils.h"
31 #include "snapshot_fuzz_utils.h"
32
33 using android::base::Error;
34 using android::base::GetBoolProperty;
35 using android::base::LogId;
36 using android::base::LogSeverity;
37 using android::base::ReadFileToString;
38 using android::base::Result;
39 using android::base::SetLogger;
40 using android::base::StderrLogger;
41 using android::base::StdioLogger;
42 using android::fs_mgr::CreateLogicalPartitionParams;
43 using android::fuzz::CheckedCast;
44 using android::snapshot::SnapshotFuzzData;
45 using android::snapshot::SnapshotFuzzEnv;
46 using chromeos_update_engine::DeltaArchiveManifest;
47 using google::protobuf::FieldDescriptor;
48 using google::protobuf::Message;
49 using google::protobuf::RepeatedPtrField;
50
51 // Avoid linking to libgsi since it needs disk I/O.
52 namespace android::gsi {
IsGsiRunning()53 bool IsGsiRunning() {
54 LOG(FATAL) << "Called IsGsiRunning";
55 __builtin_unreachable();
56 }
GetDsuSlot(const std::string & install_dir)57 std::string GetDsuSlot(const std::string& install_dir) {
58 LOG(FATAL) << "Called GetDsuSlot(" << install_dir << ")";
59 __builtin_unreachable();
60 }
61 } // namespace android::gsi
62
63 namespace android::snapshot {
64
65 const SnapshotFuzzData* current_data = nullptr;
66 const SnapshotTestModule* current_module = nullptr;
67
68 SnapshotFuzzEnv* GetSnapshotFuzzEnv();
69
70 FUZZ_CLASS(ISnapshotManager, SnapshotManagerAction);
71
72 using ProcessUpdateStateArgs = SnapshotManagerAction::Proto::ProcessUpdateStateArgs;
73 using CreateLogicalAndSnapshotPartitionsArgs =
74 SnapshotManagerAction::Proto::CreateLogicalAndSnapshotPartitionsArgs;
75 using RecoveryCreateSnapshotDevicesArgs =
76 SnapshotManagerAction::Proto::RecoveryCreateSnapshotDevicesArgs;
77
78 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, BeginUpdate);
79 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, CancelUpdate);
80 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, InitiateMerge);
81 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, NeedSnapshotsInFirstStageMount);
82 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, RecoveryCreateSnapshotDevices);
83 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted);
84 FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance);
85
86 #define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ReturnType, ...) \
87 FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, ReturnType, ISnapshotManager* snapshot, \
88 ##__VA_ARGS__)
89
SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites,bool,bool wipe)90 SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool, bool wipe) {
91 return snapshot->FinishedSnapshotWrites(wipe);
92 }
93
SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState,bool,const ProcessUpdateStateArgs & args)94 SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, bool, const ProcessUpdateStateArgs& args) {
95 std::function<bool()> before_cancel;
96 if (args.has_before_cancel()) {
97 before_cancel = [&]() { return args.fail_before_cancel(); };
98 }
99 return snapshot->ProcessUpdateState({}, before_cancel);
100 }
101
SNAPSHOT_FUZZ_FUNCTION(GetUpdateState,UpdateState,bool has_progress_arg)102 SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, UpdateState, bool has_progress_arg) {
103 double progress;
104 return snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
105 }
106
SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe,bool,bool has_callback)107 SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool, bool has_callback) {
108 std::function<void()> callback;
109 if (has_callback) {
110 callback = []() {};
111 }
112 return snapshot->HandleImminentDataWipe(callback);
113 }
114
SNAPSHOT_FUZZ_FUNCTION(Dump,bool)115 SNAPSHOT_FUZZ_FUNCTION(Dump, bool) {
116 std::stringstream ss;
117 return snapshot->Dump(ss);
118 }
119
SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots,bool,const DeltaArchiveManifest & manifest)120 SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, bool, const DeltaArchiveManifest& manifest) {
121 return snapshot->CreateUpdateSnapshots(manifest);
122 }
123
SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot,bool,const std::string & name)124 SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, bool, const std::string& name) {
125 return snapshot->UnmapUpdateSnapshot(name);
126 }
127
SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions,bool,const CreateLogicalAndSnapshotPartitionsArgs & args)128 SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, bool,
129 const CreateLogicalAndSnapshotPartitionsArgs& args) {
130 const std::string* super;
131 if (args.use_correct_super()) {
132 super = &GetSnapshotFuzzEnv()->super();
133 } else {
134 super = &args.super();
135 }
136 return snapshot->CreateLogicalAndSnapshotPartitions(
137 *super, std::chrono::milliseconds(args.timeout_millis()));
138 }
139
SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata,CreateResult,const RecoveryCreateSnapshotDevicesArgs & args)140 SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, CreateResult,
141 const RecoveryCreateSnapshotDevicesArgs& args) {
142 std::unique_ptr<AutoDevice> device;
143 if (args.has_metadata_device_object()) {
144 device = std::make_unique<NoOpAutoDevice>(args.metadata_mounted());
145 }
146 return snapshot->RecoveryCreateSnapshotDevices(device);
147 }
148
SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot,bool,const CreateLogicalPartitionParamsProto & params_proto)149 SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, bool,
150 const CreateLogicalPartitionParamsProto& params_proto) {
151 auto partition_opener = std::make_unique<TestPartitionOpener>(GetSnapshotFuzzEnv()->super());
152 CreateLogicalPartitionParams params;
153 if (params_proto.use_correct_super()) {
154 params.block_device = GetSnapshotFuzzEnv()->super();
155 } else {
156 params.block_device = params_proto.block_device();
157 }
158 if (params_proto.has_metadata_slot()) {
159 params.metadata_slot = params_proto.metadata_slot();
160 }
161 params.partition_name = params_proto.partition_name();
162 params.force_writable = params_proto.force_writable();
163 params.timeout_ms = std::chrono::milliseconds(params_proto.timeout_millis());
164 params.device_name = params_proto.device_name();
165 params.partition_opener = partition_opener.get();
166 std::string path;
167 return snapshot->MapUpdateSnapshot(params, &path);
168 }
169
SNAPSHOT_FUZZ_FUNCTION(SwitchSlot,void)170 SNAPSHOT_FUZZ_FUNCTION(SwitchSlot, void) {
171 (void)snapshot;
172 CHECK(current_module != nullptr);
173 CHECK(current_module->device_info != nullptr);
174 current_module->device_info->SwitchSlot();
175 }
176
177 // During global init, log all messages to stdio. This is only done once.
AllowLoggingDuringGlobalInit()178 int AllowLoggingDuringGlobalInit() {
179 SetLogger(&StdioLogger);
180 return 0;
181 }
182
183 // Only log fatal messages during tests.
FatalOnlyLogger(LogId logid,LogSeverity severity,const char * tag,const char * file,unsigned int line,const char * message)184 void FatalOnlyLogger(LogId logid, LogSeverity severity, const char* tag, const char* file,
185 unsigned int line, const char* message) {
186 if (severity == LogSeverity::FATAL) {
187 StderrLogger(logid, severity, tag, file, line, message);
188
189 // If test fails by a LOG(FATAL) or CHECK(), log the corpus. If it abort()'s, there's
190 // nothing else we can do.
191 StderrLogger(logid, severity, tag, __FILE__, __LINE__,
192 "Attempting to dump current corpus:");
193 if (current_data == nullptr) {
194 StderrLogger(logid, severity, tag, __FILE__, __LINE__, "Current corpus is nullptr.");
195 } else {
196 std::string content;
197 if (!google::protobuf::TextFormat::PrintToString(*current_data, &content)) {
198 StderrLogger(logid, severity, tag, __FILE__, __LINE__,
199 "Failed to print corpus to string.");
200 } else {
201 StderrLogger(logid, severity, tag, __FILE__, __LINE__, content.c_str());
202 }
203 }
204 }
205 }
206 // Stop logging (except fatal messages) after global initialization. This is only done once.
StopLoggingAfterGlobalInit()207 int StopLoggingAfterGlobalInit() {
208 (void)GetSnapshotFuzzEnv();
209 [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silencer;
210 SetLogger(&FatalOnlyLogger);
211 return 0;
212 }
213
GetSnapshotFuzzEnv()214 SnapshotFuzzEnv* GetSnapshotFuzzEnv() {
215 [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
216 static SnapshotFuzzEnv env;
217 return &env;
218 }
219
SetUpTest(const SnapshotFuzzData & snapshot_fuzz_data)220 SnapshotTestModule SetUpTest(const SnapshotFuzzData& snapshot_fuzz_data) {
221 current_data = &snapshot_fuzz_data;
222
223 auto env = GetSnapshotFuzzEnv();
224 env->CheckSoftReset();
225
226 auto test_module = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
227 current_module = &test_module;
228 CHECK(test_module.snapshot);
229 return test_module;
230 }
231
TearDownTest()232 void TearDownTest() {
233 current_module = nullptr;
234 current_data = nullptr;
235 }
236
237 } // namespace android::snapshot
238
DEFINE_PROTO_FUZZER(const SnapshotFuzzData & snapshot_fuzz_data)239 DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
240 using namespace android::snapshot;
241
242 [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
243 auto test_module = SetUpTest(snapshot_fuzz_data);
244 SnapshotManagerAction::ExecuteAll(test_module.snapshot.get(), snapshot_fuzz_data.actions());
245 TearDownTest();
246 }
247
248 namespace android::snapshot {
249
250 // Work-around to cast a 'void' value to Result<void>.
251 template <typename T>
252 struct GoodResult {
253 template <typename F>
Castandroid::snapshot::GoodResult254 static Result<T> Cast(F&& f) {
255 return f();
256 }
257 };
258
259 template <>
260 struct GoodResult<void> {
261 template <typename F>
Castandroid::snapshot::GoodResult262 static Result<void> Cast(F&& f) {
263 f();
264 return {};
265 }
266 };
267
268 class LibsnapshotFuzzerTest : public ::testing::Test {
269 protected:
SetUpTestCase()270 static void SetUpTestCase() {
271 // Do initialization once.
272 (void)GetSnapshotFuzzEnv();
273 }
SetUp()274 void SetUp() override {
275 bool is_virtual_ab = GetBoolProperty("ro.virtual_ab.enabled", false);
276 if (!is_virtual_ab) GTEST_SKIP() << "Test only runs on Virtual A/B devices.";
277 }
SetUpFuzzData(const std::string & fn)278 void SetUpFuzzData(const std::string& fn) {
279 auto path = android::base::GetExecutableDirectory() + "/corpus/"s + fn;
280 std::string proto_text;
281 ASSERT_TRUE(ReadFileToString(path, &proto_text));
282 snapshot_fuzz_data_ = std::make_unique<SnapshotFuzzData>();
283 ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(proto_text,
284 snapshot_fuzz_data_.get()));
285 test_module_ = android::snapshot::SetUpTest(*snapshot_fuzz_data_);
286 }
TearDown()287 void TearDown() override { android::snapshot::TearDownTest(); }
288 template <typename FuzzFunction>
Execute(int action_index)289 Result<typename FuzzFunction::ReturnType> Execute(int action_index) {
290 if (action_index >= snapshot_fuzz_data_->actions_size()) {
291 return Error() << "Index " << action_index << " is out of bounds ("
292 << snapshot_fuzz_data_->actions_size() << " actions in corpus";
293 }
294 const auto& action_proto = snapshot_fuzz_data_->actions(action_index);
295 const auto* field_desc =
296 android::fuzz::GetValueFieldDescriptor<typename FuzzFunction::ActionType>(
297 action_proto);
298 if (field_desc == nullptr) {
299 return Error() << "Action at index " << action_index << " has no value defined.";
300 }
301 if (FuzzFunction::tag != field_desc->number()) {
302 return Error() << "Action at index " << action_index << " is expected to be "
303 << FuzzFunction::name << ", but it is " << field_desc->name()
304 << " in corpus.";
305 }
306 return GoodResult<typename FuzzFunction::ReturnType>::Cast([&]() {
307 return android::fuzz::ActionPerformer<FuzzFunction>::Invoke(test_module_.snapshot.get(),
308 action_proto, field_desc);
309 });
310 }
311
312 std::unique_ptr<SnapshotFuzzData> snapshot_fuzz_data_;
313 SnapshotTestModule test_module_;
314 };
315
316 #define SNAPSHOT_FUZZ_FN_NAME(name) FUZZ_FUNCTION_CLASS_NAME(SnapshotManagerAction, name)
317
318 MATCHER_P(ResultIs, expected, "") {
319 if (!arg.ok()) {
320 *result_listener << arg.error();
321 return false;
322 }
323 *result_listener << "expected: " << expected;
324 return arg.value() == expected;
325 }
326
327 #define ASSERT_RESULT_TRUE(actual) ASSERT_THAT(actual, ResultIs(true))
328
329 // Check that launch_device.txt is executed correctly.
TEST_F(LibsnapshotFuzzerTest,LaunchDevice)330 TEST_F(LibsnapshotFuzzerTest, LaunchDevice) {
331 SetUpFuzzData("launch_device.txt");
332
333 int i = 0;
334 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(BeginUpdate)>(i++));
335 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateUpdateSnapshots)>(i++));
336 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "sys_b";
337 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "vnd_b";
338 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "prd_b";
339 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(FinishedSnapshotWrites)>(i++));
340 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "sys_b";
341 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "vnd_b";
342 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "prd_b";
343 ASSERT_RESULT_OK(Execute<SNAPSHOT_FUZZ_FN_NAME(SwitchSlot)>(i++));
344 ASSERT_EQ("_b", test_module_.device_info->GetSlotSuffix());
345 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(NeedSnapshotsInFirstStageMount)>(i++));
346 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateLogicalAndSnapshotPartitions)>(i++));
347 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(InitiateMerge)>(i++));
348 ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(ProcessUpdateState)>(i++));
349 ASSERT_EQ(i, snapshot_fuzz_data_->actions_size()) << "Not all actions are executed.";
350 }
351
352 } // namespace android::snapshot
353