libsnapshot: Reject bad cow versions.
Remove CowWriter::GetCowVersion. Instead, reject any update that asks
for VABC but has an unsupported version field. Do not fallback to legacy
VAB as the update was likely built improperly.
Bug: 280529365
Test: vts_libsnapshot_test
Change-Id: Ibc0f981801fd47bf39d7a19944134e4b3c66e5bf
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 9f94699..b228dff 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -28,6 +28,9 @@
static constexpr uint32_t kCowVersionManifest = 2;
+static constexpr uint32_t kMinCowVersion = 1;
+static constexpr uint32_t kMaxCowVersion = 2;
+
// This header appears as the first sequence of bytes in the COW. All fields
// in the layout are little-endian encoded. The on-disk layout is:
//
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 4151b01..8458517 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -171,8 +171,6 @@
uint64_t GetCowSize() override;
- uint32_t GetCowVersion() { return header_.prefix.major_version; }
-
protected:
virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 2661482..b0c4a83 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -3202,39 +3202,20 @@
CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
- std::map<std::string, SnapshotStatus> all_snapshot_status;
-
- // In case of error, automatically delete devices that are created along the way.
- // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
- // these devices.
- AutoDeviceList created_devices;
-
const auto& dap_metadata = manifest.dynamic_partition_metadata();
- CowOptions options;
- CowWriter writer(options);
- bool cow_format_support = true;
- if (dap_metadata.cow_version() < writer.GetCowVersion()) {
- cow_format_support = false;
- }
-
- LOG(INFO) << " dap_metadata.cow_version(): " << dap_metadata.cow_version()
- << " writer.GetCowVersion(): " << writer.GetCowVersion();
-
- // Deduce supported features.
- bool userspace_snapshots = CanUseUserspaceSnapshots();
- bool legacy_compression = GetLegacyCompressionEnabledProperty();
std::string vabc_disable_reason;
if (!dap_metadata.vabc_enabled()) {
vabc_disable_reason = "not enabled metadata";
} else if (device_->IsRecovery()) {
vabc_disable_reason = "recovery";
- } else if (!cow_format_support) {
- vabc_disable_reason = "cow format not supported";
} else if (!KernelSupportsCompressedSnapshots()) {
vabc_disable_reason = "kernel missing userspace block device support";
}
+ // Deduce supported features.
+ bool userspace_snapshots = CanUseUserspaceSnapshots();
+ bool legacy_compression = GetLegacyCompressionEnabledProperty();
if (!vabc_disable_reason.empty()) {
if (userspace_snapshots) {
LOG(INFO) << "Userspace snapshots disabled: " << vabc_disable_reason;
@@ -3246,6 +3227,16 @@
legacy_compression = false;
}
+ if (legacy_compression || userspace_snapshots) {
+ if (dap_metadata.cow_version() < kMinCowVersion ||
+ dap_metadata.cow_version() > kMaxCowVersion) {
+ LOG(ERROR) << "Manifest cow version is out of bounds (got: "
+ << dap_metadata.cow_version() << ", min: " << kMinCowVersion
+ << ", max: " << kMaxCowVersion << ")";
+ return Return::Error();
+ }
+ }
+
const bool using_snapuserd = userspace_snapshots || legacy_compression;
if (!using_snapuserd) {
LOG(INFO) << "Using legacy Virtual A/B (dm-snapshot)";
@@ -3278,6 +3269,11 @@
cow_creator.batched_writes = dap_metadata.vabc_feature_set().batch_writes();
}
+ // In case of error, automatically delete devices that are created along the way.
+ // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
+ // these devices.
+ AutoDeviceList created_devices;
+ std::map<std::string, SnapshotStatus> all_snapshot_status;
auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
&all_snapshot_status);
if (!ret.is_ok()) {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 22731e7..f08f913 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2688,6 +2688,24 @@
ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
}
+TEST_F(SnapshotUpdateTest, BadCowVersion) {
+ if (!snapuserd_required_) {
+ GTEST_SKIP() << "VABC only";
+ }
+
+ ASSERT_TRUE(sm->BeginUpdate());
+
+ auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
+ dynamic_partition_metadata->set_cow_version(kMinCowVersion - 1);
+ ASSERT_FALSE(sm->CreateUpdateSnapshots(manifest_));
+
+ dynamic_partition_metadata->set_cow_version(kMaxCowVersion + 1);
+ ASSERT_FALSE(sm->CreateUpdateSnapshots(manifest_));
+
+ dynamic_partition_metadata->set_cow_version(kMaxCowVersion);
+ ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+}
+
class FlashAfterUpdateTest : public SnapshotUpdateTest,
public WithParamInterface<std::tuple<uint32_t, bool>> {
public: