Revert "update_engine: Deprecate major version 1" This partially reverts commit 55c75417e22d5026971276997924a345d9973bbc. It turns out that we forgot a scenario when we deprecated major version 1. We use update_engine in lab tests (specifically autoupdate_EndToEndTests on stable channel) to update a DUT to an old (very old) versions using actual update payloads so we can test that they can get updated to newer versions. However, deprecating major version 1 in the update_engine caused trouble because we no longer can update from a newer version to a version before M72 (to prepare the device for update test). We need to put this feature back until we find a better solution for it. On this CL, we only support major version 1 in the client and only for test (non-official) images. We don't even bother adding paygen support for it. This CL should be reverted once we figured out what to do with provisioning the autoupdate end to end tests. BUG=chromium:1043428 TEST=FEATURES=test emerge-reef update_engine TEST=cros deployed it, then cros flash using an m71 payload, it succeeded. Change-Id: I1fecbe3ae845b2e419f0999adc53e4732b1f7696 Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/2013884 Reviewed-by: Tianjie Xu <[email protected]> Reviewed-by: Sen Jiang <[email protected]> Tested-by: Amin Hassani <[email protected]> Commit-Queue: Amin Hassani <[email protected]> (cherry picked from commit fac20229289cf4d4373fffe83037d44b780eabd0) Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/2016088 Reviewed-by: Amin Hassani <[email protected]>
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc index ee5f38c..ef7bd9e 100644 --- a/payload_consumer/delta_performer.cc +++ b/payload_consumer/delta_performer.cc
@@ -437,7 +437,7 @@ if (!IsHeaderParsed()) { MetadataParseResult result = - payload_metadata_.ParsePayloadHeader(payload, error); + payload_metadata_.ParsePayloadHeader(payload, hardware_, error); if (result != MetadataParseResult::kSuccess) return result; @@ -713,7 +713,8 @@ // In major version 2, we don't add dummy operation to the payload. // If we already extracted the signature we should skip this step. - if (manifest_.has_signatures_offset() && manifest_.has_signatures_size() && + if (major_payload_version_ == kBrilloMajorPayloadVersion && + manifest_.has_signatures_offset() && manifest_.has_signatures_size() && signatures_message_data_.empty()) { if (manifest_.signatures_offset() != buffer_offset_) { LOG(ERROR) << "Payload signatures offset points to blob offset " @@ -748,11 +749,51 @@ } bool DeltaPerformer::ParseManifestPartitions(ErrorCode* error) { - partitions_.clear(); - for (const PartitionUpdate& partition : manifest_.partitions()) { - partitions_.push_back(partition); + if (major_payload_version_ == kBrilloMajorPayloadVersion) { + partitions_.clear(); + for (const PartitionUpdate& partition : manifest_.partitions()) { + partitions_.push_back(partition); + } + manifest_.clear_partitions(); + } else if (major_payload_version_ == kChromeOSMajorPayloadVersion) { + LOG(INFO) << "Converting update information from old format."; + PartitionUpdate root_part; + root_part.set_partition_name(kPartitionNameRoot); +#ifdef __ANDROID__ + LOG(WARNING) << "Legacy payload major version provided to an Android " + "build. Assuming no post-install. Please use major version " + "2 or newer."; + root_part.set_run_postinstall(false); +#else + root_part.set_run_postinstall(true); +#endif // __ANDROID__ + if (manifest_.has_old_rootfs_info()) { + *root_part.mutable_old_partition_info() = manifest_.old_rootfs_info(); + manifest_.clear_old_rootfs_info(); + } + if (manifest_.has_new_rootfs_info()) { + *root_part.mutable_new_partition_info() = manifest_.new_rootfs_info(); + manifest_.clear_new_rootfs_info(); + } + *root_part.mutable_operations() = manifest_.install_operations(); + manifest_.clear_install_operations(); + partitions_.push_back(std::move(root_part)); + + PartitionUpdate kern_part; + kern_part.set_partition_name(kPartitionNameKernel); + kern_part.set_run_postinstall(false); + if (manifest_.has_old_kernel_info()) { + *kern_part.mutable_old_partition_info() = manifest_.old_kernel_info(); + manifest_.clear_old_kernel_info(); + } + if (manifest_.has_new_kernel_info()) { + *kern_part.mutable_new_partition_info() = manifest_.new_kernel_info(); + manifest_.clear_new_kernel_info(); + } + *kern_part.mutable_operations() = manifest_.kernel_install_operations(); + manifest_.clear_kernel_install_operations(); + partitions_.push_back(std::move(kern_part)); } - manifest_.clear_partitions(); // Fill in the InstallPlan::partitions based on the partitions from the // payload. @@ -913,6 +954,14 @@ TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset()); TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length()); + // Extract the signature message if it's in this operation. + if (ExtractSignatureMessageFromOperation(operation)) { + // If this is dummy replace operation, we ignore it after extracting the + // signature. + DiscardBuffer(true, 0); + return true; + } + // Setup the ExtentWriter stack based on the operation type. std::unique_ptr<ExtentWriter> writer = std::make_unique<DirectExtentWriter>(); @@ -1363,6 +1412,19 @@ return true; } +bool DeltaPerformer::ExtractSignatureMessageFromOperation( + const InstallOperation& operation) { + if (operation.type() != InstallOperation::REPLACE || + !manifest_.has_signatures_offset() || + manifest_.signatures_offset() != operation.data_offset()) { + return false; + } + TEST_AND_RETURN_FALSE(manifest_.has_signatures_size() && + manifest_.signatures_size() == operation.data_length()); + TEST_AND_RETURN_FALSE(ExtractSignatureMessage()); + return true; +} + bool DeltaPerformer::ExtractSignatureMessage() { TEST_AND_RETURN_FALSE(signatures_message_data_.empty()); TEST_AND_RETURN_FALSE(buffer_offset_ == manifest_.signatures_offset()); @@ -1414,11 +1476,11 @@ // Perform assorted checks to sanity check the manifest, make sure it // matches data from other sources, and that it is a supported version. - bool has_old_fields = std::any_of(manifest_.partitions().begin(), - manifest_.partitions().end(), - [](const PartitionUpdate& partition) { - return partition.has_old_partition_info(); - }); + bool has_old_fields = + (manifest_.has_old_kernel_info() || manifest_.has_old_rootfs_info()); + for (const PartitionUpdate& partition : manifest_.partitions()) { + has_old_fields = has_old_fields || partition.has_old_partition_info(); + } // The presence of an old partition hash is the sole indicator for a delta // update. @@ -1460,12 +1522,16 @@ } } - if (manifest_.has_old_rootfs_info() || manifest_.has_new_rootfs_info() || - manifest_.has_old_kernel_info() || manifest_.has_new_kernel_info() || - manifest_.install_operations_size() != 0 || - manifest_.kernel_install_operations_size() != 0) { - LOG(ERROR) << "Manifest contains deprecated fields."; - return ErrorCode::kPayloadMismatchedType; + if (major_payload_version_ != kChromeOSMajorPayloadVersion) { + if (manifest_.has_old_rootfs_info() || manifest_.has_new_rootfs_info() || + manifest_.has_old_kernel_info() || manifest_.has_new_kernel_info() || + manifest_.install_operations_size() != 0 || + manifest_.kernel_install_operations_size() != 0) { + LOG(ERROR) << "Manifest contains deprecated field only supported in " + << "major payload version 1, but the payload major version is " + << major_payload_version_; + return ErrorCode::kPayloadMismatchedType; + } } if (manifest_.max_timestamp() < hardware_->GetBuildTimestamp()) { @@ -1476,6 +1542,16 @@ return ErrorCode::kPayloadTimestampError; } + if (major_payload_version_ == kChromeOSMajorPayloadVersion) { + if (manifest_.has_dynamic_partition_metadata()) { + LOG(ERROR) + << "Should not contain dynamic_partition_metadata for major version " + << kChromeOSMajorPayloadVersion + << ". Please use major version 2 or above."; + return ErrorCode::kPayloadMismatchedType; + } + } + // TODO(crbug.com/37661) we should be adding more and more manifest checks, // such as partition boundaries, etc.
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h index 7860747..4493c2a 100644 --- a/payload_consumer/delta_performer.h +++ b/payload_consumer/delta_performer.h
@@ -237,6 +237,11 @@ FileDescriptorPtr ChooseSourceFD(const InstallOperation& operation, ErrorCode* error); + // Extracts the payload signature message from the blob on the |operation| if + // the offset matches the one specified by the manifest. Returns whether the + // signature was extracted. + bool ExtractSignatureMessageFromOperation(const InstallOperation& operation); + // Extracts the payload signature message from the current |buffer_| if the // offset matches the one specified by the manifest. Returns whether the // signature was extracted.
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc index 5f55739..80cae86 100644 --- a/payload_consumer/delta_performer_integration_test.cc +++ b/payload_consumer/delta_performer_integration_test.cc
@@ -591,7 +591,8 @@ { EXPECT_TRUE(utils::ReadFile(state->delta_path, &state->delta)); PayloadMetadata payload_metadata; - EXPECT_TRUE(payload_metadata.ParsePayloadHeader(state->delta)); + EXPECT_TRUE(payload_metadata.ParsePayloadHeader(state->delta, + &state->fake_hardware_)); state->metadata_size = payload_metadata.GetMetadataSize(); LOG(INFO) << "Metadata size: " << state->metadata_size; state->metadata_signature_size =
diff --git a/payload_consumer/payload_constants.cc b/payload_consumer/payload_constants.cc index 908a893..4015a0a 100644 --- a/payload_consumer/payload_constants.cc +++ b/payload_consumer/payload_constants.cc
@@ -20,7 +20,7 @@ namespace chromeos_update_engine { -// const uint64_t kChromeOSMajorPayloadVersion = 1; DEPRECATED +const uint64_t kChromeOSMajorPayloadVersion = 1; const uint64_t kBrilloMajorPayloadVersion = 2; const uint64_t kMinSupportedMajorPayloadVersion = kBrilloMajorPayloadVersion;
diff --git a/payload_consumer/payload_constants.h b/payload_consumer/payload_constants.h index 888fa2a..fe823f4 100644 --- a/payload_consumer/payload_constants.h +++ b/payload_consumer/payload_constants.h
@@ -26,7 +26,7 @@ namespace chromeos_update_engine { // The major version used by Chrome OS. -// extern const uint64_t kChromeOSMajorPayloadVersion; DEPRECATED +extern const uint64_t kChromeOSMajorPayloadVersion; // The major version used by Brillo. extern const uint64_t kBrilloMajorPayloadVersion;
diff --git a/payload_consumer/payload_metadata.cc b/payload_consumer/payload_metadata.cc index 4d8ee7b..4f16740 100644 --- a/payload_consumer/payload_metadata.cc +++ b/payload_consumer/payload_metadata.cc
@@ -20,6 +20,7 @@ #include <brillo/data_encoding.h> +#include "update_engine/common/hardware_interface.h" #include "update_engine/common/hash_calculator.h" #include "update_engine/common/utils.h" #include "update_engine/payload_consumer/payload_constants.h" @@ -36,18 +37,36 @@ const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8; const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4; -uint64_t PayloadMetadata::GetMetadataSignatureSizeOffset() const { - return kDeltaManifestSizeOffset + kDeltaManifestSizeSize; +bool PayloadMetadata::GetMetadataSignatureSizeOffset( + uint64_t* out_offset) const { + if (GetMajorVersion() == kBrilloMajorPayloadVersion) { + *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize; + return true; + } + return false; } -uint64_t PayloadMetadata::GetManifestOffset() const { - // Actual manifest begins right after the metadata signature size field. - return kDeltaManifestSizeOffset + kDeltaManifestSizeSize + - kDeltaMetadataSignatureSizeSize; +bool PayloadMetadata::GetManifestOffset(uint64_t* out_offset) const { + // Actual manifest begins right after the manifest size field or + // metadata signature size field if major version >= 2. + if (major_payload_version_ == kChromeOSMajorPayloadVersion) { + *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize; + return true; + } + if (major_payload_version_ == kBrilloMajorPayloadVersion) { + *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize + + kDeltaMetadataSignatureSizeSize; + return true; + } + LOG(ERROR) << "Unknown major payload version: " << major_payload_version_; + return false; } MetadataParseResult PayloadMetadata::ParsePayloadHeader( - const brillo::Blob& payload, ErrorCode* error) { + const brillo::Blob& payload, + HardwareInterface* hardware, + ErrorCode* error) { + uint64_t manifest_offset; // Ensure we have data to cover the major payload version. if (payload.size() < kDeltaManifestSizeOffset) return MetadataParseResult::kInsufficientData; @@ -59,11 +78,6 @@ return MetadataParseResult::kError; } - uint64_t manifest_offset = GetManifestOffset(); - // Check again with the manifest offset. - if (payload.size() < manifest_offset) - return MetadataParseResult::kInsufficientData; - // Extract the payload version from the metadata. static_assert(sizeof(major_payload_version_) == kDeltaVersionSize, "Major payload version size mismatch"); @@ -73,14 +87,26 @@ // Switch big endian to host. major_payload_version_ = be64toh(major_payload_version_); - if (major_payload_version_ < kMinSupportedMajorPayloadVersion || - major_payload_version_ > kMaxSupportedMajorPayloadVersion) { + // We only want to test major version 1 for test images. + if (major_payload_version_ == kChromeOSMajorPayloadVersion + ? hardware != nullptr && hardware->IsOfficialBuild() + : major_payload_version_ < kMinSupportedMajorPayloadVersion || + major_payload_version_ > kMaxSupportedMajorPayloadVersion) { LOG(ERROR) << "Bad payload format -- unsupported payload version: " << major_payload_version_; *error = ErrorCode::kUnsupportedMajorPayloadVersion; return MetadataParseResult::kError; } + // Get the manifest offset now that we have payload version. + if (!GetManifestOffset(&manifest_offset)) { + *error = ErrorCode::kUnsupportedMajorPayloadVersion; + return MetadataParseResult::kError; + } + // Check again with the manifest offset. + if (payload.size() < manifest_offset) + return MetadataParseResult::kInsufficientData; + // Next, parse the manifest size. static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize, "manifest_size size mismatch"); @@ -96,32 +122,43 @@ return MetadataParseResult::kError; } - // Parse the metadata signature size. - static_assert( - sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize, - "metadata_signature_size size mismatch"); - uint64_t metadata_signature_size_offset = GetMetadataSignatureSizeOffset(); - memcpy(&metadata_signature_size_, - &payload[metadata_signature_size_offset], - kDeltaMetadataSignatureSizeSize); - metadata_signature_size_ = be32toh(metadata_signature_size_); + if (GetMajorVersion() == kBrilloMajorPayloadVersion) { + // Parse the metadata signature size. + static_assert( + sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize, + "metadata_signature_size size mismatch"); + uint64_t metadata_signature_size_offset; + if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) { + *error = ErrorCode::kError; + return MetadataParseResult::kError; + } + memcpy(&metadata_signature_size_, + &payload[metadata_signature_size_offset], + kDeltaMetadataSignatureSizeSize); + metadata_signature_size_ = be32toh(metadata_signature_size_); - if (metadata_size_ + metadata_signature_size_ < metadata_size_) { - // Overflow detected. - *error = ErrorCode::kDownloadInvalidMetadataSize; - return MetadataParseResult::kError; + if (metadata_size_ + metadata_signature_size_ < metadata_size_) { + // Overflow detected. + LOG(ERROR) << "Overflow detected on metadata and signature size."; + *error = ErrorCode::kDownloadInvalidMetadataSize; + return MetadataParseResult::kError; + } } return MetadataParseResult::kSuccess; } -bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) { +bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload, + HardwareInterface* hardware) { ErrorCode error; - return ParsePayloadHeader(payload, &error) == MetadataParseResult::kSuccess; + return ParsePayloadHeader(payload, hardware, &error) == + MetadataParseResult::kSuccess; } bool PayloadMetadata::GetManifest(const brillo::Blob& payload, DeltaArchiveManifest* out_manifest) const { - uint64_t manifest_offset = GetManifestOffset(); + uint64_t manifest_offset; + if (!GetManifestOffset(&manifest_offset)) + return false; CHECK_GE(payload.size(), manifest_offset + manifest_size_); return out_manifest->ParseFromArray(&payload[manifest_offset], manifest_size_); @@ -143,7 +180,7 @@ << metadata_signature; return ErrorCode::kDownloadMetadataSignatureError; } - } else { + } else if (major_payload_version_ == kBrilloMajorPayloadVersion) { metadata_signature_protobuf_blob.assign( payload.begin() + metadata_size_, payload.begin() + metadata_size_ + metadata_signature_size_); @@ -204,7 +241,7 @@ brillo::Blob payload; TEST_AND_RETURN_FALSE( utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload)); - TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload)); + TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload, nullptr)); if (manifest != nullptr) { TEST_AND_RETURN_FALSE( @@ -215,7 +252,8 @@ TEST_AND_RETURN_FALSE(GetManifest(payload, manifest)); } - if (metadata_signatures != nullptr) { + if (metadata_signatures != nullptr && + GetMajorVersion() >= kBrilloMajorPayloadVersion) { payload.clear(); TEST_AND_RETURN_FALSE(utils::ReadFileChunk( payload_path, GetMetadataSize(), GetMetadataSignatureSize(), &payload));
diff --git a/payload_consumer/payload_metadata.h b/payload_consumer/payload_metadata.h index be43c41..3292351 100644 --- a/payload_consumer/payload_metadata.h +++ b/payload_consumer/payload_metadata.h
@@ -26,6 +26,7 @@ #include <brillo/secure_blob.h> #include "update_engine/common/error_code.h" +#include "update_engine/common/hardware_interface.h" #include "update_engine/common/platform_constants.h" #include "update_engine/update_metadata.pb.h" @@ -54,9 +55,11 @@ // metadata. Returns kMetadataParseError if the metadata can't be parsed given // the payload. MetadataParseResult ParsePayloadHeader(const brillo::Blob& payload, + HardwareInterface* hardware, ErrorCode* error); // Simpler version of the above, returns true on success. - bool ParsePayloadHeader(const brillo::Blob& payload); + bool ParsePayloadHeader(const brillo::Blob& payload, + HardwareInterface* hardware); // Given the |payload|, verifies that the signed hash of its metadata matches // |metadata_signature| (if present) or the metadata signature in payload @@ -94,12 +97,14 @@ Signatures* metadata_signatures); private: - // Returns the byte offset at which the manifest protobuf begins in a payload. - uint64_t GetManifestOffset() const; + // Set |*out_offset| to the byte offset at which the manifest protobuf begins + // in a payload. Return true on success, false if the offset is unknown. + bool GetManifestOffset(uint64_t* out_offset) const; - // Returns the byte offset where the size of the metadata signature is stored - // in a payload. - uint64_t GetMetadataSignatureSizeOffset() const; + // Set |*out_offset| to the byte offset where the size of the metadata + // signature is stored in a payload. Return true on success, if this field is + // not present in the payload, return false. + bool GetMetadataSignatureSizeOffset(uint64_t* out_offset) const; uint64_t metadata_size_{0}; uint64_t manifest_size_{0};
diff --git a/payload_generator/payload_signer.cc b/payload_generator/payload_signer.cc index 420329f..613202d 100644 --- a/payload_generator/payload_signer.cc +++ b/payload_generator/payload_signer.cc
@@ -94,7 +94,7 @@ brillo::Blob payload; TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload)); PayloadMetadata payload_metadata; - TEST_AND_RETURN_FALSE(payload_metadata.ParsePayloadHeader(payload)); + TEST_AND_RETURN_FALSE(payload_metadata.ParsePayloadHeader(payload, nullptr)); uint64_t metadata_size = payload_metadata.GetMetadataSize(); uint32_t metadata_signature_size = payload_metadata.GetMetadataSignatureSize(); @@ -218,7 +218,7 @@ brillo::Blob payload; TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload)); PayloadMetadata payload_metadata; - TEST_AND_RETURN_FALSE(payload_metadata.ParsePayloadHeader(payload)); + TEST_AND_RETURN_FALSE(payload_metadata.ParsePayloadHeader(payload, nullptr)); DeltaArchiveManifest manifest; TEST_AND_RETURN_FALSE(payload_metadata.GetManifest(payload, &manifest)); TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() &&
diff --git a/update_attempter_android.cc b/update_attempter_android.cc index c738e4e..e2b5a88 100644 --- a/update_attempter_android.cc +++ b/update_attempter_android.cc
@@ -368,7 +368,7 @@ } ErrorCode errorcode; PayloadMetadata payload_metadata; - if (payload_metadata.ParsePayloadHeader(metadata, &errorcode) != + if (payload_metadata.ParsePayloadHeader(metadata, nullptr, &errorcode) != MetadataParseResult::kSuccess) { return LogAndSetError(error, FROM_HERE,