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