AU: Verify that the applied delta update matches the server-sent hashes.
Also, don't try to resume any delta update that fails for any reason
other than download transfer errors.
BUG=7348
TEST=unit tests, gmerged on device
Change-Id: Ice464b8d421256717d7909fd5fa46d762bd48952
Review URL: http://codereview.chromium.org/3599025
diff --git a/action_processor.h b/action_processor.h
index b2c1392..fc42a24 100644
--- a/action_processor.h
+++ b/action_processor.h
@@ -36,6 +36,7 @@
kActionCodeDownloadHashMismatchError = 10,
kActionCodeDownloadSizeMismatchError = 11,
kActionCodeDownloadPayloadVerificationError = 12,
+ kActionCodeDownloadAppliedUpdateVerificationError = 13,
};
class AbstractAction;
diff --git a/delta_performer.cc b/delta_performer.cc
index 3c24d5b..7ee9d4f 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -545,8 +545,6 @@
key_path,
&signed_hash_data));
OmahaHashCalculator signed_hasher;
- // TODO(petkov): Make sure signed_hash_context_ is loaded when resuming an
- // update.
TEST_AND_RETURN_FALSE(signed_hasher.SetContext(signed_hash_context_));
TEST_AND_RETURN_FALSE(signed_hasher.Finalize());
const vector<char>& hash_data = signed_hasher.raw_hash();
@@ -555,6 +553,28 @@
return true;
}
+bool DeltaPerformer::VerifyAppliedUpdate(const string& path,
+ const string& kernel_path) {
+ LOG(INFO) << "Verifying applied update.";
+ TEST_AND_RETURN_FALSE(manifest_valid_ &&
+ manifest_.has_new_kernel_info() &&
+ manifest_.has_new_rootfs_info());
+ const string* paths[] = { &kernel_path, &path };
+ const PartitionInfo* infos[] = {
+ &manifest_.new_kernel_info(), &manifest_.new_rootfs_info()
+ };
+ for (size_t i = 0; i < arraysize(paths); ++i) {
+ OmahaHashCalculator hasher;
+ TEST_AND_RETURN_FALSE(hasher.UpdateFile(*paths[i], infos[i]->size()));
+ TEST_AND_RETURN_FALSE(hasher.Finalize());
+ TEST_AND_RETURN_FALSE(hasher.raw_hash().size() == infos[i]->hash().size());
+ TEST_AND_RETURN_FALSE(memcmp(hasher.raw_hash().data(),
+ infos[i]->hash().data(),
+ hasher.raw_hash().size()) == 0);
+ }
+ return true;
+}
+
void DeltaPerformer::DiscardBufferHeadBytes(size_t count) {
hash_calculator_.Update(&buffer_[0], count);
buffer_.erase(buffer_.begin(), buffer_.begin() + count);
diff --git a/delta_performer.h b/delta_performer.h
index 16073d8..7394f94 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -63,6 +63,11 @@
const std::string& update_check_response_hash,
const uint64_t update_check_response_size);
+ // Verifies that the generated update is correct based on the hashes sent by
+ // the server. Returns true on success, false otherwise.
+ bool VerifyAppliedUpdate(const std::string& path,
+ const std::string& kernel_path);
+
// Converts an ordered collection of Extent objects which contain data of
// length full_length to a comma-separated string. For each Extent, the
// string will have the start offset and then the length in bytes.
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index b8d6554..a3c43f6 100755
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -286,6 +286,7 @@
EXPECT_EQ(0, performer.Close());
CompareFilesByBlock(old_kernel, new_kernel);
+ CompareFilesByBlock(a_img, b_img);
vector<char> updated_kernel_partition;
EXPECT_TRUE(utils::ReadFile(old_kernel, &updated_kernel_partition));
@@ -297,6 +298,7 @@
kUnittestPublicKeyPath,
OmahaHashCalculator::OmahaHashOfData(delta),
delta.size()));
+ EXPECT_TRUE(performer.VerifyAppliedUpdate(a_img, old_kernel));
}
TEST(DeltaPerformerTest, NewFullUpdateTest) {
diff --git a/download_action.cc b/download_action.cc
index b88e448..2920d16 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -147,6 +147,11 @@
LOG(ERROR) << "Download of " << install_plan_.download_url
<< " failed due to payload verification error.";
code = kActionCodeDownloadPayloadVerificationError;
+ } else if (!delta_performer_->VerifyAppliedUpdate(
+ install_plan_.install_path, install_plan_.kernel_install_path)) {
+ LOG(ERROR) << "Download of " << install_plan_.download_url
+ << " failed due to applied update verification error.";
+ code = kActionCodeDownloadAppliedUpdateVerificationError;
}
} else {
// Makes sure the hash and size are correct for an old-style full update.
diff --git a/update_attempter.cc b/update_attempter.cc
index f408d82..1e3bf5d 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -519,11 +519,8 @@
void UpdateAttempter::MarkDeltaUpdateFailure() {
CHECK(!is_full_update_);
- // If a delta update fails after the downloading phase, don't try to resume it
- // the next time.
- if (status_ > UPDATE_STATUS_DOWNLOADING) {
- DeltaPerformer::ResetUpdateProgress(prefs_, false);
- }
+ // Don't try to resume a failed delta update.
+ DeltaPerformer::ResetUpdateProgress(prefs_, false);
int64_t delta_failures;
if (!prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) ||
delta_failures < 0) {
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index b8d85a1..bb86af7 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -17,6 +17,7 @@
using testing::_;
using testing::DoAll;
using testing::InSequence;
+using testing::Ne;
using testing::Property;
using testing::Return;
using testing::SetArgumentPointee;
@@ -131,6 +132,8 @@
.WillOnce(DoAll(
SetArgumentPointee<1>(UpdateAttempter::kMaxDeltaUpdateFailures),
Return(true)));
+ EXPECT_CALL(prefs_, SetInt64(Ne(kPrefsDeltaUpdateFailures), _))
+ .WillRepeatedly(Return(true));
EXPECT_CALL(prefs_, SetInt64(kPrefsDeltaUpdateFailures, 1)).Times(2);
EXPECT_CALL(prefs_, SetInt64(kPrefsDeltaUpdateFailures, 2)).Times(1);
EXPECT_CALL(prefs_, SetInt64(kPrefsDeltaUpdateFailures,