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 <ftw.h>
16 #include <inttypes.h>
17 #include <sys/mman.h>
18 #include <sys/mount.h>
19 #include <sys/stat.h>
20 #include <sysexits.h>
21 
22 #include <chrono>
23 #include <string>
24 
25 #include <android-base/file.h>
26 #include <android-base/logging.h>
27 #include <android-base/properties.h>
28 #include <android-base/stringprintf.h>
29 #include <android-base/strings.h>
30 #include <fs_mgr.h>
31 #include <libsnapshot/auto_device.h>
32 #include <libsnapshot/snapshot.h>
33 #include <storage_literals/storage_literals.h>
34 
35 #include "snapshot_fuzz_utils.h"
36 #include "utility.h"
37 
38 // Prepends the errno string, but it is good enough.
39 #ifndef PCHECK
40 #define PCHECK(x) CHECK(x) << strerror(errno) << ": "
41 #endif
42 
43 using namespace android::storage_literals;
44 using namespace std::chrono_literals;
45 using namespace std::string_literals;
46 
47 using android::base::Basename;
48 using android::base::ReadFileToString;
49 using android::base::SetProperty;
50 using android::base::Split;
51 using android::base::StartsWith;
52 using android::base::StringPrintf;
53 using android::base::unique_fd;
54 using android::base::WriteStringToFile;
55 using android::dm::DeviceMapper;
56 using android::dm::DmTarget;
57 using android::dm::LoopControl;
58 using android::fiemap::IImageManager;
59 using android::fiemap::ImageManager;
60 using android::fs_mgr::BlockDeviceInfo;
61 using android::fs_mgr::FstabEntry;
62 using android::fs_mgr::IPartitionOpener;
63 using chromeos_update_engine::DynamicPartitionMetadata;
64 
65 static const char MNT_DIR[] = "/mnt";
66 static const char BLOCK_SYSFS[] = "/sys/block";
67 
68 static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
69 static const auto SUPER_IMAGE_SIZE = 16_MiB;
70 static const auto DATA_IMAGE_SIZE = 16_MiB;
71 static const auto FAKE_ROOT_SIZE = 64_MiB;
72 
73 namespace android::snapshot {
74 
Mkdir(const std::string & path)75 bool Mkdir(const std::string& path) {
76     if (mkdir(path.c_str(), 0750) == -1 && errno != EEXIST) {
77         PLOG(ERROR) << "Cannot create " << path;
78         return false;
79     }
80     return true;
81 }
82 
RmdirRecursive(const std::string & path)83 bool RmdirRecursive(const std::string& path) {
84     auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
85         switch (file_type) {
86             case FTW_D:
87             case FTW_DP:
88             case FTW_DNR:
89                 if (rmdir(child) == -1) {
90                     PLOG(ERROR) << "rmdir " << child;
91                     return -1;
92                 }
93                 return 0;
94             case FTW_NS:
95             default:
96                 if (rmdir(child) != -1) break;
97                 [[fallthrough]];
98             case FTW_F:
99             case FTW_SL:
100             case FTW_SLN:
101                 if (unlink(child) == -1) {
102                     PLOG(ERROR) << "unlink " << child;
103                     return -1;
104                 }
105                 return 0;
106         }
107         return 0;
108     };
109 
110     return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0;
111 }
112 
GetLinearBaseDeviceString(const DeviceMapper::TargetInfo & target)113 std::string GetLinearBaseDeviceString(const DeviceMapper::TargetInfo& target) {
114     if (target.spec.target_type != "linear"s) return {};
115     auto tokens = Split(target.data, " ");
116     CHECK_EQ(2, tokens.size());
117     return tokens[0];
118 }
119 
GetSnapshotBaseDeviceStrings(const DeviceMapper::TargetInfo & target)120 std::vector<std::string> GetSnapshotBaseDeviceStrings(const DeviceMapper::TargetInfo& target) {
121     if (target.spec.target_type != "snapshot"s && target.spec.target_type != "snapshot-merge"s)
122         return {};
123     auto tokens = Split(target.data, " ");
124     CHECK_EQ(4, tokens.size());
125     return {tokens[0], tokens[1]};
126 }
127 
ShouldDeleteLoopDevice(const std::string & node)128 bool ShouldDeleteLoopDevice(const std::string& node) {
129     std::string backing_file;
130     if (ReadFileToString(StringPrintf("%s/loop/backing_file", node.data()), &backing_file)) {
131         if (StartsWith(backing_file, std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME)) {
132             return true;
133         }
134     }
135     return false;
136 }
137 
GetTableInfoIfExists(const std::string & dev_name)138 std::vector<DeviceMapper::TargetInfo> GetTableInfoIfExists(const std::string& dev_name) {
139     auto& dm = DeviceMapper::Instance();
140     std::vector<DeviceMapper::TargetInfo> table;
141     if (!dm.GetTableInfo(dev_name, &table)) {
142         PCHECK(errno == ENODEV);
143         return {};
144     }
145     return table;
146 }
147 
GetAllBaseDeviceStrings(const std::string & child_dev)148 std::set<std::string> GetAllBaseDeviceStrings(const std::string& child_dev) {
149     std::set<std::string> ret;
150     for (const auto& child_target : GetTableInfoIfExists(child_dev)) {
151         auto snapshot_bases = GetSnapshotBaseDeviceStrings(child_target);
152         ret.insert(snapshot_bases.begin(), snapshot_bases.end());
153 
154         auto linear_base = GetLinearBaseDeviceString(child_target);
155         if (!linear_base.empty()) {
156             ret.insert(linear_base);
157         }
158     }
159     return ret;
160 }
161 
162 using PropertyList = std::set<std::string>;
InsertProperty(const char * key,const char *,void * cookie)163 void InsertProperty(const char* key, const char* /*name*/, void* cookie) {
164     reinterpret_cast<PropertyList*>(cookie)->insert(key);
165 }
166 
167 // Attempt to delete all devices that is based on dev_name, including itself.
CheckDeleteDeviceMapperTree(const std::string & dev_name,bool known_allow_delete=false,uint64_t depth=100)168 void CheckDeleteDeviceMapperTree(const std::string& dev_name, bool known_allow_delete = false,
169                                  uint64_t depth = 100) {
170     CHECK(depth > 0) << "Reaching max depth when deleting " << dev_name
171                      << ". There may be devices referencing itself. Check `dmctl list devices -v`.";
172 
173     auto& dm = DeviceMapper::Instance();
174     auto table = GetTableInfoIfExists(dev_name);
175     if (table.empty()) {
176         PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
177         return;
178     }
179 
180     if (!known_allow_delete) {
181         for (const auto& target : table) {
182             auto base_device_string = GetLinearBaseDeviceString(target);
183             if (base_device_string.empty()) continue;
184             if (ShouldDeleteLoopDevice(
185                         StringPrintf("/sys/dev/block/%s", base_device_string.data()))) {
186                 known_allow_delete = true;
187                 break;
188             }
189         }
190     }
191     if (!known_allow_delete) {
192         return;
193     }
194 
195     std::string dev_string;
196     PCHECK(dm.GetDeviceString(dev_name, &dev_string));
197 
198     std::vector<DeviceMapper::DmBlockDevice> devices;
199     PCHECK(dm.GetAvailableDevices(&devices));
200     for (const auto& child_dev : devices) {
201         auto child_bases = GetAllBaseDeviceStrings(child_dev.name());
202         if (child_bases.find(dev_string) != child_bases.end()) {
203             CheckDeleteDeviceMapperTree(child_dev.name(), true /* known_allow_delete */, depth - 1);
204         }
205     }
206 
207     PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
208 }
209 
210 // Attempt to clean up residues from previous runs.
CheckCleanupDeviceMapperDevices()211 void CheckCleanupDeviceMapperDevices() {
212     auto& dm = DeviceMapper::Instance();
213     std::vector<DeviceMapper::DmBlockDevice> devices;
214     PCHECK(dm.GetAvailableDevices(&devices));
215 
216     for (const auto& dev : devices) {
217         CheckDeleteDeviceMapperTree(dev.name());
218     }
219 }
220 
CheckUmount(const std::string & path)221 void CheckUmount(const std::string& path) {
222     PCHECK(TEMP_FAILURE_RETRY(umount(path.data()) == 0) || errno == ENOENT || errno == EINVAL)
223             << path;
224 }
225 
CheckDetachLoopDevices(const std::set<std::string> & exclude_names={})226 void CheckDetachLoopDevices(const std::set<std::string>& exclude_names = {}) {
227     // ~SnapshotFuzzEnv automatically does the following.
228     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(BLOCK_SYSFS), closedir);
229     PCHECK(dir != nullptr) << BLOCK_SYSFS;
230     LoopControl loop_control;
231     dirent* dp;
232     while ((dp = readdir(dir.get())) != nullptr) {
233         if (exclude_names.find(dp->d_name) != exclude_names.end()) {
234             continue;
235         }
236         if (!ShouldDeleteLoopDevice(StringPrintf("%s/%s", BLOCK_SYSFS, dp->d_name).data())) {
237             continue;
238         }
239         PCHECK(loop_control.Detach(StringPrintf("/dev/block/%s", dp->d_name).data()));
240     }
241 }
242 
CheckUmountAll()243 void CheckUmountAll() {
244     CheckUmount(std::string(MNT_DIR) + "/snapshot_fuzz_data");
245     CheckUmount(std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME);
246 }
247 
248 class AutoDeleteDir : public AutoDevice {
249   public:
New(const std::string & path)250     static std::unique_ptr<AutoDeleteDir> New(const std::string& path) {
251         if (!Mkdir(path)) {
252             return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(""));
253         }
254         return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(path));
255     }
~AutoDeleteDir()256     ~AutoDeleteDir() {
257         if (!HasDevice()) return;
258         PCHECK(rmdir(name_.c_str()) == 0 || errno == ENOENT) << name_;
259     }
260 
261   private:
AutoDeleteDir(const std::string & path)262     AutoDeleteDir(const std::string& path) : AutoDevice(path) {}
263 };
264 
265 class AutoUnmount : public AutoDevice {
266   public:
~AutoUnmount()267     ~AutoUnmount() {
268         if (!HasDevice()) return;
269         CheckUmount(name_);
270     }
AutoUnmount(const std::string & path)271     AutoUnmount(const std::string& path) : AutoDevice(path) {}
272 };
273 
274 class AutoUnmountTmpfs : public AutoUnmount {
275   public:
New(const std::string & path,uint64_t size)276     static std::unique_ptr<AutoUnmount> New(const std::string& path, uint64_t size) {
277         if (mount("tmpfs", path.c_str(), "tmpfs", 0,
278                   (void*)StringPrintf("size=%" PRIu64, size).data()) == -1) {
279             PLOG(ERROR) << "Cannot mount " << path;
280             return std::unique_ptr<AutoUnmount>(new AutoUnmount(""));
281         }
282         return std::unique_ptr<AutoUnmount>(new AutoUnmount(path));
283     }
284   private:
285     using AutoUnmount::AutoUnmount;
286 };
287 
288 // A directory on tmpfs. Upon destruct, it is unmounted and deleted.
289 class AutoMemBasedDir : public AutoDevice {
290   public:
New(const std::string & name,uint64_t size)291     static std::unique_ptr<AutoMemBasedDir> New(const std::string& name, uint64_t size) {
292         auto ret = std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(name));
293         ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path());
294         if (!ret->auto_delete_mount_dir_->HasDevice()) {
295             return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
296         }
297         ret->auto_umount_mount_point_ = AutoUnmountTmpfs::New(ret->mount_path(), size);
298         if (!ret->auto_umount_mount_point_->HasDevice()) {
299             return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
300         }
301         // tmp_path() and persist_path does not need to be deleted upon destruction, hence it is
302         // not wrapped with AutoDeleteDir.
303         if (!Mkdir(ret->tmp_path())) {
304             return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
305         }
306         if (!Mkdir(ret->persist_path())) {
307             return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
308         }
309         return ret;
310     }
311     // Return the temporary scratch directory.
tmp_path() const312     std::string tmp_path() const {
313         CHECK(HasDevice());
314         return mount_path() + "/tmp";
315     }
316     // Return the temporary scratch directory.
persist_path() const317     std::string persist_path() const {
318         CHECK(HasDevice());
319         return mount_path() + "/persist";
320     }
321     // Delete all contents in tmp_path() and start over. tmp_path() itself is re-created.
CheckSoftReset()322     void CheckSoftReset() {
323         PCHECK(RmdirRecursive(tmp_path()));
324         PCHECK(Mkdir(tmp_path()));
325     }
326 
327   private:
AutoMemBasedDir(const std::string & name)328     AutoMemBasedDir(const std::string& name) : AutoDevice(name) {}
mount_path() const329     std::string mount_path() const {
330         CHECK(HasDevice());
331         return MNT_DIR + "/"s + name_;
332     }
333     std::unique_ptr<AutoDeleteDir> auto_delete_mount_dir_;
334     std::unique_ptr<AutoUnmount> auto_umount_mount_point_;
335 };
336 
SnapshotFuzzEnv()337 SnapshotFuzzEnv::SnapshotFuzzEnv() {
338     CheckCleanupDeviceMapperDevices();
339     CheckDetachLoopDevices();
340     CheckUmountAll();
341 
342     fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE);
343     CHECK(fake_root_ != nullptr);
344     CHECK(fake_root_->HasDevice());
345     loop_control_ = std::make_unique<LoopControl>();
346 
347     fake_data_mount_point_ = MNT_DIR + "/snapshot_fuzz_data"s;
348     auto_delete_data_mount_point_ = AutoDeleteDir::New(fake_data_mount_point_);
349     CHECK(auto_delete_data_mount_point_ != nullptr);
350     CHECK(auto_delete_data_mount_point_->HasDevice());
351 
352     const auto& fake_persist_path = fake_root_->persist_path();
353     mapped_super_ = CheckMapImage(fake_persist_path + "/super.img", SUPER_IMAGE_SIZE,
354                                   loop_control_.get(), &fake_super_);
355     mapped_data_ = CheckMapImage(fake_persist_path + "/data.img", DATA_IMAGE_SIZE,
356                                  loop_control_.get(), &fake_data_block_device_);
357     mounted_data_ = CheckMountFormatData(fake_data_block_device_, fake_data_mount_point_);
358 }
359 
~SnapshotFuzzEnv()360 SnapshotFuzzEnv::~SnapshotFuzzEnv() {
361     CheckCleanupDeviceMapperDevices();
362     mounted_data_ = nullptr;
363     auto_delete_data_mount_point_ = nullptr;
364     mapped_data_ = nullptr;
365     mapped_super_ = nullptr;
366     CheckDetachLoopDevices();
367     loop_control_ = nullptr;
368     fake_root_ = nullptr;
369     CheckUmountAll();
370 }
371 
CheckZeroFill(const std::string & file,size_t size)372 void CheckZeroFill(const std::string& file, size_t size) {
373     std::string zeros(size, '\0');
374     PCHECK(WriteStringToFile(zeros, file)) << "Cannot write zeros to " << file;
375 }
376 
CheckSoftReset()377 void SnapshotFuzzEnv::CheckSoftReset() {
378     fake_root_->CheckSoftReset();
379     CheckZeroFill(super(), SUPER_IMAGE_SIZE);
380     CheckCleanupDeviceMapperDevices();
381     CheckDetachLoopDevices({Basename(fake_super_), Basename(fake_data_block_device_)});
382 }
383 
CheckCreateFakeImageManager()384 std::unique_ptr<IImageManager> SnapshotFuzzEnv::CheckCreateFakeImageManager() {
385     auto metadata_dir = fake_root_->tmp_path() + "/images_manager_metadata";
386     auto data_dir = fake_data_mount_point_ + "/image_manager_data";
387     PCHECK(Mkdir(metadata_dir));
388     PCHECK(Mkdir(data_dir));
389     return SnapshotFuzzImageManager::Open(metadata_dir, data_dir);
390 }
391 
392 // Helper to create a loop device for a file.
CheckCreateLoopDevice(LoopControl * control,const std::string & file,const std::chrono::milliseconds & timeout_ms,std::string * path)393 static void CheckCreateLoopDevice(LoopControl* control, const std::string& file,
394                                   const std::chrono::milliseconds& timeout_ms, std::string* path) {
395     static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
396     android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
397     PCHECK(file_fd >= 0) << "Could not open file: " << file;
398     CHECK(control->Attach(file_fd, timeout_ms, path))
399             << "Could not create loop device for: " << file;
400 }
401 
402 class AutoDetachLoopDevice : public AutoDevice {
403   public:
AutoDetachLoopDevice(LoopControl * control,const std::string & device)404     AutoDetachLoopDevice(LoopControl* control, const std::string& device)
405         : AutoDevice(device), control_(control) {}
~AutoDetachLoopDevice()406     ~AutoDetachLoopDevice() { PCHECK(control_->Detach(name_)) << name_; }
407 
408   private:
409     LoopControl* control_;
410 };
411 
CheckMapImage(const std::string & img_path,uint64_t size,LoopControl * control,std::string * mapped_path)412 std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMapImage(const std::string& img_path,
413                                                            uint64_t size, LoopControl* control,
414                                                            std::string* mapped_path) {
415     CheckZeroFill(img_path, size);
416     CheckCreateLoopDevice(control, img_path, 1s, mapped_path);
417 
418     return std::make_unique<AutoDetachLoopDevice>(control, *mapped_path);
419 }
420 
CheckCreateSnapshotManager(const SnapshotFuzzData & data)421 SnapshotTestModule SnapshotFuzzEnv::CheckCreateSnapshotManager(const SnapshotFuzzData& data) {
422     SnapshotTestModule ret;
423     auto partition_opener = std::make_unique<TestPartitionOpener>(super());
424     ret.opener = partition_opener.get();
425     CheckWriteSuperMetadata(data, *partition_opener);
426     auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata";
427     PCHECK(Mkdir(metadata_dir));
428     if (data.has_metadata_snapshots_dir()) {
429         PCHECK(Mkdir(metadata_dir + "/snapshots"));
430     }
431 
432     ret.device_info = new SnapshotFuzzDeviceInfo(this, data.device_info_data(),
433                                                  std::move(partition_opener), metadata_dir);
434     auto snapshot = SnapshotManager::New(ret.device_info /* takes ownership */);
435     ret.snapshot = std::move(snapshot);
436 
437     return ret;
438 }
439 
super() const440 const std::string& SnapshotFuzzEnv::super() const {
441     return fake_super_;
442 }
443 
CheckWriteSuperMetadata(const SnapshotFuzzData & data,const IPartitionOpener & opener)444 void SnapshotFuzzEnv::CheckWriteSuperMetadata(const SnapshotFuzzData& data,
445                                               const IPartitionOpener& opener) {
446     if (!data.is_super_metadata_valid()) {
447         // Leave it zero.
448         return;
449     }
450 
451     BlockDeviceInfo super_device("super", SUPER_IMAGE_SIZE, 0, 0, 4096);
452     std::vector<BlockDeviceInfo> devices = {super_device};
453     auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
454     CHECK(builder != nullptr);
455 
456     // Attempt to create a super partition metadata using proto. All errors are ignored.
457     for (const auto& group_proto : data.super_data().dynamic_partition_metadata().groups()) {
458         (void)builder->AddGroup(group_proto.name(), group_proto.size());
459         for (const auto& partition_name : group_proto.partition_names()) {
460             (void)builder->AddPartition(partition_name, group_proto.name(),
461                                         LP_PARTITION_ATTR_READONLY);
462         }
463     }
464 
465     for (const auto& partition_proto : data.super_data().partitions()) {
466         auto p = builder->FindPartition(partition_proto.partition_name());
467         if (p == nullptr) continue;
468         (void)builder->ResizePartition(p, partition_proto.new_partition_info().size());
469     }
470 
471     auto metadata = builder->Export();
472     // metadata may be nullptr if it is not valid (e.g. partition name too long).
473     // In this case, just use empty super partition data.
474     if (metadata == nullptr) {
475         builder = MetadataBuilder::New(devices, "super", 65536, 2);
476         CHECK(builder != nullptr);
477         metadata = builder->Export();
478         CHECK(metadata != nullptr);
479     }
480     CHECK(FlashPartitionTable(opener, super(), *metadata.get()));
481 }
482 
CheckMountFormatData(const std::string & blk_device,const std::string & mount_point)483 std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMountFormatData(const std::string& blk_device,
484                                                                   const std::string& mount_point) {
485     FstabEntry entry{
486             .blk_device = blk_device,
487             .length = static_cast<off64_t>(DATA_IMAGE_SIZE),
488             .fs_type = "ext4",
489             .mount_point = mount_point,
490     };
491     CHECK(0 == fs_mgr_do_format(entry, false /* crypt_footer */));
492     CHECK(0 == fs_mgr_do_mount_one(entry));
493     return std::make_unique<AutoUnmount>(mount_point);
494 }
495 
~SnapshotFuzzImageManager()496 SnapshotFuzzImageManager::~SnapshotFuzzImageManager() {
497     // Remove relevant gsid.mapped_images.* props.
498     for (const auto& name : mapped_) {
499         CHECK(UnmapImageIfExists(name)) << "Cannot unmap " << name;
500     }
501 }
502 
MapImageDevice(const std::string & name,const std::chrono::milliseconds & timeout_ms,std::string * path)503 bool SnapshotFuzzImageManager::MapImageDevice(const std::string& name,
504                                               const std::chrono::milliseconds& timeout_ms,
505                                               std::string* path) {
506     if (impl_->MapImageDevice(name, timeout_ms, path)) {
507         mapped_.insert(name);
508         return true;
509     }
510     return false;
511 }
512 
513 }  // namespace android::snapshot
514