Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5eb2d6fa authored by Yifan Hong's avatar Yifan Hong
Browse files

libsnapshot_fuzzer: map super image

... instead of operating on the image file directly.
Test: run it
Bug: 154633114

Change-Id: Id04c0d15d0d52483647716f8bfb0b8ee1a2876d9
parent c9412363
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -125,14 +125,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
    static FuzzSnapshotManager fuzz_snapshot_manager;
    [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();

    CHECK(env.InitOk());
    env.CheckSoftReset();
    FuzzData fuzz_data(data, size);

    auto snapshot_manager_data = fuzz_data.Consume<SnapshotManagerFuzzData>();
    if (!snapshot_manager_data.has_value()) {
        return 0;
    }
    auto snapshot_manager = env.CreateSnapshotManager(snapshot_manager_data.value());
    auto snapshot_manager = env.CheckCreateSnapshotManager(snapshot_manager_data.value());
    CHECK(snapshot_manager);

    fuzz_snapshot_manager.DepleteData(snapshot_manager.get(), &fuzz_data);
+84 −40
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <sys/stat.h>
#include <sysexits.h>

#include <chrono>
#include <string>

#include <android-base/file.h>
@@ -29,17 +30,27 @@
#include <storage_literals/storage_literals.h>

#include "snapshot_fuzz_utils.h"
#include "utility.h"

// Prepends the errno string, but it is good enough.
#ifndef PCHECK
#define PCHECK(x) CHECK(x) << strerror(errno) << ": "
#endif

using namespace android::storage_literals;
using namespace std::chrono_literals;
using namespace std::string_literals;

using android::base::StringPrintf;
using android::base::unique_fd;
using android::base::WriteStringToFile;
using android::dm::LoopControl;
using android::fiemap::IImageManager;
using android::fiemap::ImageManager;

static const char MNT_DIR[] = "/mnt";
// This directory is exempted from pinning in ImageManager.
static const char MNT_DIR[] = "/data/gsi/ota/test/";

static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
static const auto SUPER_IMAGE_SIZE = 16_MiB;
static const auto FAKE_ROOT_SIZE = 64_MiB;
@@ -128,6 +139,9 @@ class AutoUnmount : public AutoDevice {
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()) {
@@ -137,20 +151,31 @@ class AutoMemBasedDir : public AutoDevice {
        if (!ret->auto_umount_mount_point_->HasDevice()) {
            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
        }
        // path() does not need to be deleted upon destruction, hence it is not wrapped with
        // AutoDeleteDir.
        if (!Mkdir(ret->path())) {
        // tmp_path() and persist_path does not need to be deleted upon destruction, hence it is
        // not wrapped with AutoDeleteDir.
        if (!Mkdir(ret->tmp_path())) {
            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
        }
        if (!Mkdir(ret->persist_path())) {
            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
        }
        return ret;
    }
    // Return the scratch directory.
    std::string path() const {
    // Return the temporary scratch directory.
    std::string tmp_path() const {
        CHECK(HasDevice());
        return mount_path() + "/tmp";
    }
    // Return the temporary scratch directory.
    std::string persist_path() const {
        CHECK(HasDevice());
        return mount_path() + "/root";
        return mount_path() + "/persist";
    }
    // Delete all contents in tmp_path() and start over. tmp_path() itself is re-created.
    void CheckSoftReset() {
        PCHECK(RmdirRecursive(tmp_path()));
        PCHECK(Mkdir(tmp_path()));
    }
    // Delete all contents in path() and start over. path() itself is re-created.
    bool SoftReset() { return RmdirRecursive(path()) && Mkdir(path()); }

  private:
    AutoMemBasedDir(const std::string& name) : AutoDevice(name) {}
@@ -164,64 +189,83 @@ class AutoMemBasedDir : public AutoDevice {

SnapshotFuzzEnv::SnapshotFuzzEnv() {
    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_);
}

SnapshotFuzzEnv::~SnapshotFuzzEnv() = default;

bool SnapshotFuzzEnv::InitOk() const {
    if (fake_root_ == nullptr || !fake_root_->HasDevice()) return false;
    return true;
void CheckZeroFill(const std::string& file, size_t size) {
    std::string zeros(size, '\0');
    PCHECK(WriteStringToFile(zeros, file)) << "Cannot write zeros to " << file;
}

bool SnapshotFuzzEnv::SoftReset() {
    return fake_root_->SoftReset();
void SnapshotFuzzEnv::CheckSoftReset() {
    fake_root_->CheckSoftReset();
    CheckZeroFill(super(), SUPER_IMAGE_SIZE);
}

std::unique_ptr<IImageManager> SnapshotFuzzEnv::CreateFakeImageManager(
        const std::string& fake_root) {
    auto images_dir = fake_root + "/images";
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";

    if (!Mkdir(images_dir) || !Mkdir(metadata_dir) || !Mkdir(data_dir)) {
        return nullptr;
    }
    PCHECK(Mkdir(images_dir));
    PCHECK(Mkdir(metadata_dir));
    PCHECK(Mkdir(data_dir));
    return ImageManager::Open(metadata_dir, data_dir);
}

std::unique_ptr<TestPartitionOpener> SnapshotFuzzEnv::CreatePartitionOpener(
        const std::string& fake_root) {
    auto fake_super = fake_root + "/super.img";
    std::string zeros(SUPER_IMAGE_SIZE, '\0');

    if (!WriteStringToFile(zeros, fake_super)) {
        PLOG(ERROR) << "Cannot write zeros to " << fake_super;
        return nullptr;
// Helper to create a loop device for a file.
static void CheckCreateLoopDevice(LoopControl* control, const std::string& file,
                                  const std::chrono::milliseconds& timeout_ms, std::string* path) {
    static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
    android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
    PCHECK(file_fd >= 0) << "Could not open file: " << file;
    CHECK(control->Attach(file_fd, timeout_ms, path))
            << "Could not create loop device for: " << file;
}

    return std::make_unique<TestPartitionOpener>(fake_super);
}
class AutoDetachLoopDevice : public AutoDevice {
  public:
    AutoDetachLoopDevice(LoopControl* control, const std::string& device)
        : AutoDevice(device), control_(control) {}
    ~AutoDetachLoopDevice() { control_->Detach(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::string SnapshotFuzzEnv::root() const {
    CHECK(InitOk());
    return fake_root_->path();
    return std::make_unique<AutoDetachLoopDevice>(control, *fake_super);
}

std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CreateSnapshotManager(
std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CheckCreateSnapshotManager(
        const SnapshotManagerFuzzData& data) {
    // TODO(b/154633114): create valid super partition according to fuzz data
    auto partition_opener = CreatePartitionOpener(root());
    if (partition_opener == nullptr) return nullptr;
    auto metadata_dir = root() + "/snapshot_metadata";
    if (!Mkdir(metadata_dir)) return nullptr;
    auto partition_opener = std::make_unique<TestPartitionOpener>(super());
    auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata";
    PCHECK(Mkdir(metadata_dir));

    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_ = CreateFakeImageManager(root());
    snapshot->images_ = CheckCreateFakeImageManager(fake_root_->tmp_path());
    snapshot->has_local_image_manager_ = data.is_local_image_manager;

    return snapshot;
}

const std::string& SnapshotFuzzEnv::super() const {
    return fake_super_;
}

}  // namespace android::snapshot
+17 −13
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <libdm/loop_control.h>
#include <libfiemap/image_manager.h>
#include <libsnapshot/auto_device.h>
#include <libsnapshot/test_helpers.h>
@@ -52,27 +53,30 @@ class SnapshotFuzzEnv {
    SnapshotFuzzEnv();
    ~SnapshotFuzzEnv();

    // Check if environment is initialized properly.
    bool InitOk() const;

    // A scratch directory for the test to play around with. The scratch directory
    // is backed by tmpfs. SoftReset() clears the directory.
    std::string root() const;

    // Soft reset part of the environment before running the next test.
    bool SoftReset();
    // Abort if fails.
    void CheckSoftReset();

    // 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> CreateSnapshotManager(const SnapshotManagerFuzzData& data);
    std::unique_ptr<ISnapshotManager> CheckCreateSnapshotManager(
            const SnapshotManagerFuzzData& data);

    // Return path to super partition.
    const std::string& super() const;

  private:
    std::unique_ptr<AutoMemBasedDir> fake_root_;

    static std::unique_ptr<android::fiemap::IImageManager> CreateFakeImageManager(
            const std::string& fake_root);
    static std::unique_ptr<TestPartitionOpener> CreatePartitionOpener(const std::string& fake_root);
    std::unique_ptr<android::dm::LoopControl> loop_control_;
    std::unique_ptr<AutoDevice> mapped_super_;
    std::string fake_super_;

    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,
                                                     android::dm::LoopControl* control,
                                                     std::string* fake_super);
};

class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {