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

Commit aab16613 authored by Alessio Balsini's avatar Alessio Balsini
Browse files

libsnapshot: COW size is computed within the container capability

Enforce the checking of what chunk/sector/byte is written to the COW
size calculator to avoid possible overflows in the container.
If an undesired behavior occurs, the COW computation is aborted and,
the class user will be notified with an empty returned value.

Bug: 176972301
Test: vts_libsnapshot_test
Change-Id: I29f7909780853434a09032c27c943f505c6d0d19
parent 0130a782
Loading
Loading
Loading
Loading
+44 −3
Original line number Diff line number Diff line
@@ -14,8 +14,10 @@

#pragma once

#include <android-base/logging.h>
#include <stdint.h>

#include <optional>
#include <vector>

namespace android {
@@ -31,14 +33,41 @@ class DmSnapCowSizeCalculator {
    void WriteByte(uint64_t address) { WriteSector(address / sector_bytes_); }
    void WriteSector(uint64_t sector) { WriteChunk(sector / chunk_sectors_); }
    void WriteChunk(uint64_t chunk_id) {
        if (!valid_) {
            return;
        }

        if (modified_chunks_.size() <= chunk_id) {
            if (modified_chunks_.max_size() <= chunk_id) {
                LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
                valid_ = false;
                return;
            }
            modified_chunks_.resize(chunk_id + 1, false);
            if (modified_chunks_.size() <= chunk_id) {
                LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
                valid_ = false;
                return;
            }
        }

        modified_chunks_[chunk_id] = true;
    }

    uint64_t cow_size_bytes() const { return cow_size_sectors() * sector_bytes_; }
    uint64_t cow_size_sectors() const { return cow_size_chunks() * chunk_sectors_; }
    std::optional<uint64_t> cow_size_bytes() const {
        auto sectors = cow_size_sectors();
        if (!sectors) {
            return std::nullopt;
        }
        return sectors.value() * sector_bytes_;
    }
    std::optional<uint64_t> cow_size_sectors() const {
        auto chunks = cow_size_chunks();
        if (!chunks) {
            return std::nullopt;
        }
        return chunks.value() * chunk_sectors_;
    }

    /*
     * The COW device has a precise internal structure as follows:
@@ -56,7 +85,12 @@ class DmSnapCowSizeCalculator {
     *   - chunks addressable by previous map (exceptions_per_chunk)
     * - 1 extra chunk
     */
    uint64_t cow_size_chunks() const {
    std::optional<uint64_t> cow_size_chunks() const {
        if (!valid_) {
            LOG(ERROR) << "Invalid COW size.";
            return std::nullopt;
        }

        uint64_t modified_chunks_count = 0;
        uint64_t cow_chunks = 0;

@@ -102,6 +136,13 @@ class DmSnapCowSizeCalculator {
     */
    const uint64_t exceptions_per_chunk;

    /*
     * Validity check for the container.
     * It may happen that the caller attempts the write of an invalid chunk
     * identifier, and this misbehavior is accounted and stored in this value.
     */
    bool valid_ = true;

    /*
     * |modified_chunks_| is a container that keeps trace of the modified
     * chunks.
+4 −4
Original line number Diff line number Diff line
@@ -142,11 +142,11 @@ void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Exte
    }
}

uint64_t PartitionCowCreator::GetCowSize() {
std::optional<uint64_t> PartitionCowCreator::GetCowSize() {
    if (compression_enabled) {
        if (update == nullptr || !update->has_estimate_cow_size()) {
            LOG(ERROR) << "Update manifest does not include a COW size";
            return 0;
            return std::nullopt;
        }

        // Add an extra 2MB of wiggle room for any minor differences in labels/metadata
@@ -239,7 +239,7 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
    }

    // Compute the COW partition size.
    uint64_t cow_partition_size = std::min(cow_size, free_region_length);
    uint64_t cow_partition_size = std::min(cow_size.value(), free_region_length);
    // Round it down to the nearest logical block. Logical partitions must be a multiple
    // of logical blocks.
    cow_partition_size &= ~(logical_block_size - 1);
@@ -247,7 +247,7 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
    // Assign cow_partition_usable_regions to indicate what regions should the COW partition uses.
    ret.cow_partition_usable_regions = std::move(free_regions);

    auto cow_file_size = cow_size - cow_partition_size;
    auto cow_file_size = cow_size.value() - cow_partition_size;
    // Round it up to the nearest sector.
    cow_file_size += kSectorSize - 1;
    cow_file_size &= ~(kSectorSize - 1);
+1 −1
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ struct PartitionCowCreator {

  private:
    bool HasExtent(Partition* p, Extent* e);
    uint64_t GetCowSize();
    std::optional<uint64_t> GetCowSize();
};

}  // namespace snapshot
+4 −0
Original line number Diff line number Diff line
@@ -308,6 +308,10 @@ TEST(DmSnapshotInternals, CowSizeCalculator) {
        cc.WriteByte(b);
        ASSERT_EQ(cc.cow_size_sectors(), 40);
    }

    // Write a byte that would surely overflow the counter
    cc.WriteChunk(std::numeric_limits<uint64_t>::max());
    ASSERT_FALSE(cc.cow_size_sectors().has_value());
}

void BlocksToExtents(const std::vector<uint64_t>& blocks,