Home | History | Annotate | Download | only in component
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agree to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <fcntl.h>
     18 #include <gtest/gtest.h>
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <sys/stat.h>
     22 #include <sys/statvfs.h>
     23 #include <sys/types.h>
     24 #include <time.h>
     25 
     26 #include <memory>
     27 #include <string>
     28 #include <vector>
     29 
     30 #include <android-base/file.h>
     31 #include <android-base/stringprintf.h>
     32 #include <android-base/test_utils.h>
     33 #include <bsdiff/bsdiff.h>
     34 #include <openssl/sha.h>
     35 
     36 #include "applypatch/applypatch.h"
     37 #include "applypatch/applypatch_modes.h"
     38 #include "common/test_constants.h"
     39 #include "otautil/cache_location.h"
     40 #include "otautil/print_sha1.h"
     41 
     42 using namespace std::string_literals;
     43 
     44 static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) {
     45   ASSERT_NE(nullptr, sha1);
     46 
     47   std::string data;
     48   ASSERT_TRUE(android::base::ReadFileToString(fname, &data));
     49 
     50   if (fsize != nullptr) {
     51     *fsize = data.size();
     52   }
     53 
     54   uint8_t digest[SHA_DIGEST_LENGTH];
     55   SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest);
     56   *sha1 = print_sha1(digest);
     57 }
     58 
     59 static void mangle_file(const std::string& fname) {
     60   std::string content(1024, '\0');
     61   for (size_t i = 0; i < 1024; i++) {
     62     content[i] = rand() % 256;
     63   }
     64   ASSERT_TRUE(android::base::WriteStringToFile(content, fname));
     65 }
     66 
     67 class ApplyPatchTest : public ::testing::Test {
     68  public:
     69   virtual void SetUp() override {
     70     // set up files
     71     old_file = from_testdata_base("old.file");
     72     new_file = from_testdata_base("new.file");
     73     nonexistent_file = from_testdata_base("nonexistent.file");
     74 
     75     // set up SHA constants
     76     sha1sum(old_file, &old_sha1, &old_size);
     77     sha1sum(new_file, &new_sha1, &new_size);
     78     srand(time(nullptr));
     79     bad_sha1_a = android::base::StringPrintf("%040x", rand());
     80     bad_sha1_b = android::base::StringPrintf("%040x", rand());
     81   }
     82 
     83   std::string old_file;
     84   std::string new_file;
     85   std::string nonexistent_file;
     86 
     87   std::string old_sha1;
     88   std::string new_sha1;
     89   std::string bad_sha1_a;
     90   std::string bad_sha1_b;
     91 
     92   size_t old_size;
     93   size_t new_size;
     94 };
     95 
     96 class ApplyPatchCacheTest : public ApplyPatchTest {
     97  protected:
     98   void SetUp() override {
     99     ApplyPatchTest::SetUp();
    100     CacheLocation::location().set_cache_temp_source(old_file);
    101   }
    102 };
    103 
    104 class ApplyPatchModesTest : public ::testing::Test {
    105  protected:
    106   void SetUp() override {
    107     CacheLocation::location().set_cache_temp_source(cache_source.path);
    108   }
    109 
    110   TemporaryFile cache_source;
    111 };
    112 
    113 TEST_F(ApplyPatchTest, CheckModeSkip) {
    114   std::vector<std::string> sha1s;
    115   ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
    116 }
    117 
    118 TEST_F(ApplyPatchTest, CheckModeSingle) {
    119   std::vector<std::string> sha1s = { old_sha1 };
    120   ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
    121 }
    122 
    123 TEST_F(ApplyPatchTest, CheckModeMultiple) {
    124   std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b };
    125   ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
    126 }
    127 
    128 TEST_F(ApplyPatchTest, CheckModeFailure) {
    129   std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b };
    130   ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
    131 }
    132 
    133 TEST_F(ApplyPatchTest, CheckModeEmmcTarget) {
    134   // EMMC:old_file:size:sha1 should pass the check.
    135   std::string src_file =
    136       "EMMC:" + old_file + ":" + std::to_string(old_size) + ":" + old_sha1;
    137   std::vector<std::string> sha1s;
    138   ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s));
    139 
    140   // EMMC:old_file:(size-1):sha1:(size+1):sha1 should fail the check.
    141   src_file = "EMMC:" + old_file + ":" + std::to_string(old_size - 1) + ":" + old_sha1 + ":" +
    142              std::to_string(old_size + 1) + ":" + old_sha1;
    143   ASSERT_EQ(1, applypatch_check(src_file.c_str(), sha1s));
    144 
    145   // EMMC:old_file:(size-1):sha1:size:sha1:(size+1):sha1 should pass the check.
    146   src_file = "EMMC:" + old_file + ":" +
    147              std::to_string(old_size - 1) + ":" + old_sha1 + ":" +
    148              std::to_string(old_size) + ":" + old_sha1 + ":" +
    149              std::to_string(old_size + 1) + ":" + old_sha1;
    150   ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s));
    151 
    152   // EMMC:old_file:(size+1):sha1:(size-1):sha1:size:sha1 should pass the check.
    153   src_file = "EMMC:" + old_file + ":" +
    154              std::to_string(old_size + 1) + ":" + old_sha1 + ":" +
    155              std::to_string(old_size - 1) + ":" + old_sha1 + ":" +
    156              std::to_string(old_size) + ":" + old_sha1;
    157   ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s));
    158 
    159   // EMMC:new_file:(size+1):old_sha1:(size-1):old_sha1:size:old_sha1:size:new_sha1
    160   // should pass the check.
    161   src_file = "EMMC:" + new_file + ":" +
    162              std::to_string(old_size + 1) + ":" + old_sha1 + ":" +
    163              std::to_string(old_size - 1) + ":" + old_sha1 + ":" +
    164              std::to_string(old_size) + ":" + old_sha1 + ":" +
    165              std::to_string(new_size) + ":" + new_sha1;
    166   ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s));
    167 }
    168 
    169 TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceSingle) {
    170   TemporaryFile temp_file;
    171   mangle_file(temp_file.path);
    172   std::vector<std::string> sha1s_single = { old_sha1 };
    173   ASSERT_EQ(0, applypatch_check(temp_file.path, sha1s_single));
    174   ASSERT_EQ(0, applypatch_check(nonexistent_file.c_str(), sha1s_single));
    175 }
    176 
    177 TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceMultiple) {
    178   TemporaryFile temp_file;
    179   mangle_file(temp_file.path);
    180   std::vector<std::string> sha1s_multiple = { bad_sha1_a, old_sha1, bad_sha1_b };
    181   ASSERT_EQ(0, applypatch_check(temp_file.path, sha1s_multiple));
    182   ASSERT_EQ(0, applypatch_check(nonexistent_file.c_str(), sha1s_multiple));
    183 }
    184 
    185 TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceFailure) {
    186   TemporaryFile temp_file;
    187   mangle_file(temp_file.path);
    188   std::vector<std::string> sha1s_failure = { bad_sha1_a, bad_sha1_b };
    189   ASSERT_NE(0, applypatch_check(temp_file.path, sha1s_failure));
    190   ASSERT_NE(0, applypatch_check(nonexistent_file.c_str(), sha1s_failure));
    191 }
    192 
    193 TEST_F(ApplyPatchModesTest, InvalidArgs) {
    194   // At least two args (including the filename).
    195   ASSERT_EQ(2, applypatch_modes(1, (const char* []){ "applypatch" }));
    196 
    197   // Unrecognized args.
    198   ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" }));
    199 }
    200 
    201 TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) {
    202   std::string boot_img = from_testdata_base("boot.img");
    203   size_t boot_img_size;
    204   std::string boot_img_sha1;
    205   sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
    206 
    207   std::string recovery_img = from_testdata_base("recovery.img");
    208   size_t size;
    209   std::string recovery_img_sha1;
    210   sha1sum(recovery_img, &recovery_img_sha1, &size);
    211   std::string recovery_img_size = std::to_string(size);
    212 
    213   std::string bonus_file = from_testdata_base("bonus.file");
    214 
    215   // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
    216   TemporaryFile tmp1;
    217   std::string src_file =
    218       "EMMC:" + boot_img + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
    219   std::string tgt_file = "EMMC:" + std::string(tmp1.path);
    220   std::string patch = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p");
    221   std::vector<const char*> args = {
    222     "applypatch",
    223     "-b",
    224     bonus_file.c_str(),
    225     src_file.c_str(),
    226     tgt_file.c_str(),
    227     recovery_img_sha1.c_str(),
    228     recovery_img_size.c_str(),
    229     patch.c_str()
    230   };
    231   ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
    232 
    233   // applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
    234   TemporaryFile tmp2;
    235   patch = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p");
    236   tgt_file = "EMMC:" + std::string(tmp2.path);
    237   std::vector<const char*> args2 = {
    238     "applypatch",
    239     src_file.c_str(),
    240     tgt_file.c_str(),
    241     recovery_img_sha1.c_str(),
    242     recovery_img_size.c_str(),
    243     patch.c_str()
    244   };
    245   ASSERT_EQ(0, applypatch_modes(args2.size(), args2.data()));
    246 
    247   // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> \
    248   //               <src-sha1-fake>:<patch1> <src-sha1>:<patch2>
    249   TemporaryFile tmp3;
    250   tgt_file = "EMMC:" + std::string(tmp3.path);
    251   std::string bad_sha1_a = android::base::StringPrintf("%040x", rand());
    252   std::string bad_sha1_b = android::base::StringPrintf("%040x", rand());
    253   std::string patch1 = bad_sha1_a + ":" + from_testdata_base("recovery-from-boot.p");
    254   std::string patch2 = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p");
    255   std::string patch3 = bad_sha1_b + ":" + from_testdata_base("recovery-from-boot.p");
    256   std::vector<const char*> args3 = {
    257     "applypatch",
    258     "-b",
    259     bonus_file.c_str(),
    260     src_file.c_str(),
    261     tgt_file.c_str(),
    262     recovery_img_sha1.c_str(),
    263     recovery_img_size.c_str(),
    264     patch1.c_str(),
    265     patch2.c_str(),
    266     patch3.c_str()
    267   };
    268   ASSERT_EQ(0, applypatch_modes(args3.size(), args3.data()));
    269 }
    270 
    271 // Ensures that applypatch works with a bsdiff based recovery-from-boot.p.
    272 TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithBsdiffPatch) {
    273   std::string boot_img_file = from_testdata_base("boot.img");
    274   std::string boot_img_sha1;
    275   size_t boot_img_size;
    276   sha1sum(boot_img_file, &boot_img_sha1, &boot_img_size);
    277 
    278   std::string recovery_img_file = from_testdata_base("recovery.img");
    279   std::string recovery_img_sha1;
    280   size_t recovery_img_size;
    281   sha1sum(recovery_img_file, &recovery_img_sha1, &recovery_img_size);
    282 
    283   // Generate the bsdiff patch of recovery-from-boot.p.
    284   std::string src_content;
    285   ASSERT_TRUE(android::base::ReadFileToString(boot_img_file, &src_content));
    286 
    287   std::string tgt_content;
    288   ASSERT_TRUE(android::base::ReadFileToString(recovery_img_file, &tgt_content));
    289 
    290   TemporaryFile patch_file;
    291   ASSERT_EQ(0,
    292             bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), src_content.size(),
    293                            reinterpret_cast<const uint8_t*>(tgt_content.data()), tgt_content.size(),
    294                            patch_file.path, nullptr));
    295 
    296   // applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
    297   std::string src_file_arg =
    298       "EMMC:" + boot_img_file + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
    299   TemporaryFile tgt_file;
    300   std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
    301   std::string recovery_img_size_arg = std::to_string(recovery_img_size);
    302   std::string patch_arg = boot_img_sha1 + ":" + patch_file.path;
    303   std::vector<const char*> args = { "applypatch",
    304                                     src_file_arg.c_str(),
    305                                     tgt_file_arg.c_str(),
    306                                     recovery_img_sha1.c_str(),
    307                                     recovery_img_size_arg.c_str(),
    308                                     patch_arg.c_str() };
    309   ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
    310 
    311   // Double check the patched recovery image.
    312   std::string tgt_file_sha1;
    313   size_t tgt_file_size;
    314   sha1sum(tgt_file.path, &tgt_file_sha1, &tgt_file_size);
    315   ASSERT_EQ(recovery_img_size, tgt_file_size);
    316   ASSERT_EQ(recovery_img_sha1, tgt_file_sha1);
    317 }
    318 
    319 TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) {
    320   // Invalid bonus file.
    321   ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" }));
    322 
    323   std::string bonus_file = from_testdata_base("bonus.file");
    324   // With bonus file, but missing args.
    325   ASSERT_EQ(2, applypatch_modes(3, (const char* []){ "applypatch", "-b", bonus_file.c_str() }));
    326 
    327   std::string boot_img = from_testdata_base("boot.img");
    328   size_t boot_img_size;
    329   std::string boot_img_sha1;
    330   sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
    331 
    332   std::string recovery_img = from_testdata_base("recovery.img");
    333   size_t size;
    334   std::string recovery_img_sha1;
    335   sha1sum(recovery_img, &recovery_img_sha1, &size);
    336   std::string recovery_img_size = std::to_string(size);
    337 
    338   // Bonus file is not supported in flash mode.
    339   // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size>
    340   TemporaryFile tmp4;
    341   std::vector<const char*> args4 = {
    342     "applypatch",
    343     "-b",
    344     bonus_file.c_str(),
    345     boot_img.c_str(),
    346     tmp4.path,
    347     recovery_img_sha1.c_str(),
    348     recovery_img_size.c_str()
    349   };
    350   ASSERT_NE(0, applypatch_modes(args4.size(), args4.data()));
    351 
    352   // Failed to parse patch args.
    353   TemporaryFile tmp5;
    354   std::string bad_arg1 =
    355       "invalid-sha1:filename" + from_testdata_base("recovery-from-boot-with-bonus.p");
    356   std::vector<const char*> args5 = {
    357     "applypatch",
    358     boot_img.c_str(),
    359     tmp5.path,
    360     recovery_img_sha1.c_str(),
    361     recovery_img_size.c_str(),
    362     bad_arg1.c_str()
    363   };
    364   ASSERT_NE(0, applypatch_modes(args5.size(), args5.data()));
    365 
    366   // Target size cannot be zero.
    367   TemporaryFile tmp6;
    368   std::string patch = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p");
    369   std::vector<const char*> args6 = {
    370     "applypatch",
    371     boot_img.c_str(),
    372     tmp6.path,
    373     recovery_img_sha1.c_str(),
    374     "0",  // target size
    375     patch.c_str()
    376   };
    377   ASSERT_NE(0, applypatch_modes(args6.size(), args6.data()));
    378 }
    379 
    380 TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) {
    381   // Insufficient args.
    382   ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" }));
    383 }
    384 
    385 TEST_F(ApplyPatchModesTest, ShowLicenses) {
    386   ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" }));
    387 }
    388