tests: Add a testcase for updater overrun while patching.

For any patching command, the resulting data should always exactly fill
up the given target range.

Test: Run recovery_component_test on marlin.
Change-Id: Ib3cc1fc5c11094e2eab3fe370753db51c7c4135c
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index c611c22..32fec38 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -23,6 +23,7 @@
 #include <algorithm>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <unordered_map>
 #include <vector>
 
@@ -134,9 +135,9 @@
   CloseArchive(handle);
 }
 
-static std::string get_sha1(const std::string& content) {
+static std::string GetSha1(std::string_view content) {
   uint8_t digest[SHA_DIGEST_LENGTH];
-  SHA1(reinterpret_cast<const uint8_t*>(content.c_str()), content.size(), digest);
+  SHA1(reinterpret_cast<const uint8_t*>(content.data()), content.size(), digest);
   return print_sha1(digest);
 }
 
@@ -187,7 +188,7 @@
 
     // Clear partition updated marker if any.
     std::string updated_marker{ temp_stash_base_.path };
-    updated_marker += "/" + get_sha1(image_temp_file_.path) + ".UPDATED";
+    updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED";
     ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker));
   }
 
@@ -223,14 +224,14 @@
   std::string source_content;
   ASSERT_TRUE(android::base::ReadFileToString(source_file, &source_content));
   size_t source_size = source_content.size();
-  std::string source_hash = get_sha1(source_content);
+  std::string source_hash = GetSha1(source_content);
   Partition source(source_file, source_size, source_hash);
 
   std::string target_file = from_testdata_base("recovery.img");
   std::string target_content;
   ASSERT_TRUE(android::base::ReadFileToString(target_file, &target_content));
   size_t target_size = target_content.size();
-  std::string target_hash = get_sha1(target_content);
+  std::string target_hash = GetSha1(target_content);
   Partition target(target_file, target_size, target_hash);
 
   // One argument is not valid.
@@ -619,88 +620,92 @@
   RunBlockImageUpdate(false, entries, image_file_, "", kArgsParsingFailure);
 }
 
-TEST_F(UpdaterTest, block_image_update_patch_data) {
-  std::string src_content = std::string(4096, 'a') + std::string(4096, 'c');
-  std::string tgt_content = std::string(4096, 'b') + std::string(4096, 'd');
-
+// Generates the bsdiff of the given source and target images, and writes the result entries.
+// target_blocks specifies the block count to be written into the `bsdiff` command, which may be
+// different from the given target size in order to trigger overrun / underrun paths.
+static void GetEntriesForBsdiff(std::string_view source, std::string_view target,
+                                size_t target_blocks, PackageEntries* entries) {
   // Generate the patch data.
   TemporaryFile patch_file;
-  ASSERT_EQ(0,
-            bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), src_content.size(),
-                           reinterpret_cast<const uint8_t*>(tgt_content.data()), tgt_content.size(),
-                           patch_file.path, nullptr));
+  ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(source.data()), source.size(),
+                              reinterpret_cast<const uint8_t*>(target.data()), target.size(),
+                              patch_file.path, nullptr));
   std::string patch_content;
   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch_content));
 
   // Create the transfer list that contains a bsdiff.
-  std::string src_hash = get_sha1(src_content);
-  std::string tgt_hash = get_sha1(tgt_content);
+  std::string src_hash = GetSha1(source);
+  std::string tgt_hash = GetSha1(target);
+  size_t source_blocks = source.size() / 4096;
   std::vector<std::string> transfer_list{
     // clang-format off
     "4",
-    "2",
+    std::to_string(target_blocks),
     "0",
-    "2",
-    "stash " + src_hash + " 2,0,2",
-    android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,2 2 - %s:2,0,2", patch_content.size(),
-                                src_hash.c_str(), tgt_hash.c_str(), src_hash.c_str()),
-    "free " + src_hash,
+    "0",
+    // bsdiff patch_offset patch_length source_hash target_hash target_range source_block_count
+    // source_range
+    android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,%zu %zu 2,0,%zu", patch_content.size(),
+                                src_hash.c_str(), tgt_hash.c_str(), target_blocks, source_blocks,
+                                source_blocks),
     // clang-format on
   };
 
-  PackageEntries entries{
+  *entries = {
     { "new_data", "" },
     { "patch_data", patch_content },
     { "transfer_list", android::base::Join(transfer_list, '\n') },
   };
+}
 
-  ASSERT_TRUE(android::base::WriteStringToFile(src_content, image_file_));
+TEST_F(UpdaterTest, block_image_update_patch_data) {
+  // Both source and target images have 10 blocks.
+  std::string source =
+      std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0');
+  std::string target =
+      std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0');
+  ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_));
 
+  PackageEntries entries;
+  GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2),
+                      std::string_view(target).substr(0, 4096 * 2), 2, &entries);
   RunBlockImageUpdate(false, entries, image_file_, "t");
 
   // The update_file should be patched correctly.
-  std::string updated_content;
-  ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated_content));
-  ASSERT_EQ(tgt_content, updated_content);
+  std::string updated;
+  ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated));
+  ASSERT_EQ(target, updated);
+}
+
+TEST_F(UpdaterTest, block_image_update_patch_overrun) {
+  // Both source and target images have 10 blocks.
+  std::string source =
+      std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0');
+  std::string target =
+      std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0');
+  ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_));
+
+  // Provide one less block to trigger the overrun path.
+  PackageEntries entries;
+  GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2),
+                      std::string_view(target).substr(0, 4096 * 2), 1, &entries);
+
+  // The update should fail due to overrun.
+  RunBlockImageUpdate(false, entries, image_file_, "", kPatchApplicationFailure);
 }
 
 TEST_F(UpdaterTest, block_image_update_patch_underrun) {
-  std::string src_content = std::string(4096, 'a') + std::string(4096, 'c');
-  std::string tgt_content = std::string(4096, 'b') + std::string(4096, 'd');
+  // Both source and target images have 10 blocks.
+  std::string source =
+      std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0');
+  std::string target =
+      std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0');
+  ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_));
 
-  // Generate the patch data. We intentionally provide one-byte short target to trigger the underrun
-  // path.
-  TemporaryFile patch_file;
-  ASSERT_EQ(0,
-            bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), src_content.size(),
-                           reinterpret_cast<const uint8_t*>(tgt_content.data()),
-                           tgt_content.size() - 1, patch_file.path, nullptr));
-  std::string patch_content;
-  ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch_content));
-
-  // Create the transfer list that contains a bsdiff.
-  std::string src_hash = get_sha1(src_content);
-  std::string tgt_hash = get_sha1(tgt_content);
-  std::vector<std::string> transfer_list{
-    // clang-format off
-    "4",
-    "2",
-    "0",
-    "2",
-    "stash " + src_hash + " 2,0,2",
-    android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,2 2 - %s:2,0,2", patch_content.size(),
-                                src_hash.c_str(), tgt_hash.c_str(), src_hash.c_str()),
-    "free " + src_hash,
-    // clang-format on
-  };
-
-  PackageEntries entries{
-    { "new_data", "" },
-    { "patch_data", patch_content },
-    { "transfer_list", android::base::Join(transfer_list, '\n') },
-  };
-
-  ASSERT_TRUE(android::base::WriteStringToFile(src_content, image_file_));
+  // Provide one more block to trigger the overrun path.
+  PackageEntries entries;
+  GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2),
+                      std::string_view(target).substr(0, 4096 * 2), 3, &entries);
 
   // The update should fail due to underrun.
   RunBlockImageUpdate(false, entries, image_file_, "", kPatchApplicationFailure);
@@ -708,7 +713,7 @@
 
 TEST_F(UpdaterTest, block_image_update_fail) {
   std::string src_content(4096 * 2, 'e');
-  std::string src_hash = get_sha1(src_content);
+  std::string src_hash = GetSha1(src_content);
   // Stash and free some blocks, then fail the update intentionally.
   std::vector<std::string> transfer_list{
     // clang-format off
@@ -734,7 +739,7 @@
   RunBlockImageUpdate(false, entries, image_file_, "");
 
   // Updater generates the stash name based on the input file name.
-  std::string name_digest = get_sha1(image_file_);
+  std::string name_digest = GetSha1(image_file_);
   std::string stash_base = std::string(temp_stash_base_.path) + "/" + name_digest;
   ASSERT_EQ(0, access(stash_base.c_str(), F_OK));
   // Expect the stashed blocks to be freed.
@@ -838,9 +843,9 @@
   std::string block1(4096, '1');
   std::string block2(4096, '2');
   std::string block3(4096, '3');
-  std::string block1_hash = get_sha1(block1);
-  std::string block2_hash = get_sha1(block2);
-  std::string block3_hash = get_sha1(block3);
+  std::string block1_hash = GetSha1(block1);
+  std::string block2_hash = GetSha1(block2);
+  std::string block3_hash = GetSha1(block3);
 
   // Compose the transfer list to fail the first update.
   std::vector<std::string> transfer_list_fail{
@@ -906,8 +911,8 @@
 TEST_F(UpdaterTest, last_command_update_unresumable) {
   std::string block1(4096, '1');
   std::string block2(4096, '2');
-  std::string block1_hash = get_sha1(block1);
-  std::string block2_hash = get_sha1(block2);
+  std::string block1_hash = GetSha1(block1);
+  std::string block2_hash = GetSha1(block2);
 
   // Construct an unresumable update with source blocks mismatch.
   std::vector<std::string> transfer_list_unresumable{
@@ -943,9 +948,9 @@
   std::string block1(4096, '1');
   std::string block2(4096, '2');
   std::string block3(4096, '3');
-  std::string block1_hash = get_sha1(block1);
-  std::string block2_hash = get_sha1(block2);
-  std::string block3_hash = get_sha1(block3);
+  std::string block1_hash = GetSha1(block1);
+  std::string block2_hash = GetSha1(block2);
+  std::string block3_hash = GetSha1(block3);
 
   std::vector<std::string> transfer_list_verify{
     // clang-format off
@@ -1014,7 +1019,7 @@
 
     // Clear partition updated marker if any.
     std::string updated_marker{ temp_stash_base_.path };
-    updated_marker += "/" + get_sha1(image_temp_file_.path) + ".UPDATED";
+    updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED";
     ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker));
   }
 
@@ -1045,10 +1050,10 @@
   std::string i(4096, 'i');
   std::string zero(4096, '\0');
 
-  std::string a_hash = get_sha1(a);
-  std::string b_hash = get_sha1(b);
-  std::string c_hash = get_sha1(c);
-  std::string e_hash = get_sha1(e);
+  std::string a_hash = GetSha1(a);
+  std::string b_hash = GetSha1(b);
+  std::string c_hash = GetSha1(c);
+  std::string e_hash = GetSha1(e);
 
   auto loc = [](const std::string& range_text) {
     std::vector<std::string> pieces = android::base::Split(range_text, "-");
@@ -1069,8 +1074,8 @@
   // patch 1: "b d c" -> "g"
   TemporaryFile patch_file_bdc_g;
   std::string bdc = b + d + c;
-  std::string bdc_hash = get_sha1(bdc);
-  std::string g_hash = get_sha1(g);
+  std::string bdc_hash = GetSha1(bdc);
+  std::string g_hash = GetSha1(g);
   CHECK_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(bdc.data()), bdc.size(),
                              reinterpret_cast<const uint8_t*>(g.data()), g.size(),
                              patch_file_bdc_g.path, nullptr));
@@ -1080,9 +1085,9 @@
   // patch 2: "a b c d" -> "d c b"
   TemporaryFile patch_file_abcd_dcb;
   std::string abcd = a + b + c + d;
-  std::string abcd_hash = get_sha1(abcd);
+  std::string abcd_hash = GetSha1(abcd);
   std::string dcb = d + c + b;
-  std::string dcb_hash = get_sha1(dcb);
+  std::string dcb_hash = GetSha1(dcb);
   CHECK_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(abcd.data()), abcd.size(),
                              reinterpret_cast<const uint8_t*>(dcb.data()), dcb.size(),
                              patch_file_abcd_dcb.path, nullptr));