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