libsnapshot_fuzzer: Attempt to cleanup env before and after

Ensure a clean env as much as possible. Otherwise,
previous crashes may affect subsequent runs.

Test: crash and run it again
Bug: 154633114
Change-Id: I0db690f420bef4a95d2e937f423c21ddf916e0d5
This commit is contained in:
Yifan Hong 2020-05-04 18:59:14 -07:00
parent 4cbaf858de
commit 50c52c223b
1 changed files with 176 additions and 8 deletions

View File

@ -24,7 +24,10 @@
#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>
@ -42,9 +45,16 @@ 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;
@ -54,6 +64,7 @@ using android::fs_mgr::IPartitionOpener;
using chromeos_update_engine::DynamicPartitionMetadata;
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;
@ -100,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) {
@ -110,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:
@ -123,9 +275,7 @@ class AutoUnmount : public AutoDevice {
public:
~AutoUnmount() {
if (!HasDevice()) return;
if (umount(name_.c_str()) == -1) {
PLOG(ERROR) << "Cannot umount " << name_;
}
CheckUmount(name_);
}
AutoUnmount(const std::string& path) : AutoDevice(path) {}
};
@ -194,6 +344,11 @@ 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());
@ -212,7 +367,18 @@ SnapshotFuzzEnv::SnapshotFuzzEnv() {
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');
@ -222,6 +388,8 @@ 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(
@ -245,7 +413,7 @@ 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_;