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 <openssl/sha.h>
34 
35 #include "applypatch/applypatch.h"
36 #include "applypatch/applypatch_modes.h"
37 #include "common/test_constants.h"
38 #include "print_sha1.h"
39 
sha1sum(const std::string & fname,std::string * sha1,size_t * fsize=nullptr)40 static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) {
41   ASSERT_NE(nullptr, sha1);
42 
43   std::string data;
44   ASSERT_TRUE(android::base::ReadFileToString(fname, &data));
45 
46   if (fsize != nullptr) {
47     *fsize = data.size();
48   }
49 
50   uint8_t digest[SHA_DIGEST_LENGTH];
51   SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest);
52   *sha1 = print_sha1(digest);
53 }
54 
mangle_file(const std::string & fname)55 static void mangle_file(const std::string& fname) {
56   std::string content;
57   content.reserve(1024);
58   for (size_t i = 0; i < 1024; i++) {
59     content[i] = rand() % 256;
60   }
61   ASSERT_TRUE(android::base::WriteStringToFile(content, fname));
62 }
63 
file_cmp(const std::string & f1,const std::string & f2)64 static bool file_cmp(const std::string& f1, const std::string& f2) {
65   std::string c1;
66   android::base::ReadFileToString(f1, &c1);
67   std::string c2;
68   android::base::ReadFileToString(f2, &c2);
69   return c1 == c2;
70 }
71 
72 class ApplyPatchTest : public ::testing::Test {
73  public:
SetUpTestCase()74   static void SetUpTestCase() {
75     // set up files
76     old_file = from_testdata_base("old.file");
77     new_file = from_testdata_base("new.file");
78     patch_file = from_testdata_base("patch.bsdiff");
79     rand_file = "/cache/applypatch_test_rand.file";
80     cache_file = "/cache/saved.file";
81 
82     // write stuff to rand_file
83     ASSERT_TRUE(android::base::WriteStringToFile("hello", rand_file));
84 
85     // set up SHA constants
86     sha1sum(old_file, &old_sha1, &old_size);
87     sha1sum(new_file, &new_sha1, &new_size);
88     srand(time(nullptr));
89     bad_sha1_a = android::base::StringPrintf("%040x", rand());
90     bad_sha1_b = android::base::StringPrintf("%040x", rand());
91   }
92 
93   static std::string old_file;
94   static std::string new_file;
95   static std::string rand_file;
96   static std::string cache_file;
97   static std::string patch_file;
98 
99   static std::string old_sha1;
100   static std::string new_sha1;
101   static std::string bad_sha1_a;
102   static std::string bad_sha1_b;
103 
104   static size_t old_size;
105   static size_t new_size;
106 };
107 
108 std::string ApplyPatchTest::old_file;
109 std::string ApplyPatchTest::new_file;
110 
cp(const std::string & src,const std::string & tgt)111 static void cp(const std::string& src, const std::string& tgt) {
112   std::string cmd = "cp " + src + " " + tgt;
113   system(cmd.c_str());
114 }
115 
backup_old()116 static void backup_old() {
117   cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file);
118 }
119 
restore_old()120 static void restore_old() {
121   cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file);
122 }
123 
124 class ApplyPatchCacheTest : public ApplyPatchTest {
125  public:
SetUp()126   virtual void SetUp() {
127     backup_old();
128   }
129 
TearDown()130   virtual void TearDown() {
131     restore_old();
132   }
133 };
134 
135 class ApplyPatchFullTest : public ApplyPatchCacheTest {
136  public:
SetUpTestCase()137   static void SetUpTestCase() {
138     ApplyPatchTest::SetUpTestCase();
139 
140     output_f = new TemporaryFile();
141     output_loc = std::string(output_f->path);
142 
143     struct FileContents fc;
144 
145     ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc));
146     patches.push_back(
147         std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())));
148 
149     ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc));
150     patches.push_back(
151         std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())));
152   }
153 
TearDownTestCase()154   static void TearDownTestCase() {
155     delete output_f;
156     patches.clear();
157   }
158 
159   static std::vector<std::unique_ptr<Value>> patches;
160   static TemporaryFile* output_f;
161   static std::string output_loc;
162 };
163 
164 class ApplyPatchDoubleCacheTest : public ApplyPatchFullTest {
165  public:
SetUp()166   virtual void SetUp() {
167     ApplyPatchCacheTest::SetUp();
168     cp(cache_file, "/cache/reallysaved.file");
169   }
170 
TearDown()171   virtual void TearDown() {
172     cp("/cache/reallysaved.file", cache_file);
173     ApplyPatchCacheTest::TearDown();
174   }
175 };
176 
177 std::string ApplyPatchTest::rand_file;
178 std::string ApplyPatchTest::patch_file;
179 std::string ApplyPatchTest::cache_file;
180 std::string ApplyPatchTest::old_sha1;
181 std::string ApplyPatchTest::new_sha1;
182 std::string ApplyPatchTest::bad_sha1_a;
183 std::string ApplyPatchTest::bad_sha1_b;
184 size_t ApplyPatchTest::old_size;
185 size_t ApplyPatchTest::new_size;
186 
187 std::vector<std::unique_ptr<Value>> ApplyPatchFullTest::patches;
188 TemporaryFile* ApplyPatchFullTest::output_f;
189 std::string ApplyPatchFullTest::output_loc;
190 
TEST_F(ApplyPatchTest,CheckModeSkip)191 TEST_F(ApplyPatchTest, CheckModeSkip) {
192   std::vector<std::string> sha1s;
193   ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
194 }
195 
TEST_F(ApplyPatchTest,CheckModeSingle)196 TEST_F(ApplyPatchTest, CheckModeSingle) {
197   std::vector<std::string> sha1s = { old_sha1 };
198   ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
199 }
200 
TEST_F(ApplyPatchTest,CheckModeMultiple)201 TEST_F(ApplyPatchTest, CheckModeMultiple) {
202   std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b };
203   ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
204 }
205 
TEST_F(ApplyPatchTest,CheckModeFailure)206 TEST_F(ApplyPatchTest, CheckModeFailure) {
207   std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b };
208   ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
209 }
210 
TEST_F(ApplyPatchTest,CheckModeEmmcTarget)211 TEST_F(ApplyPatchTest, CheckModeEmmcTarget) {
212   // EMMC:old_file:size:sha1 should pass the check.
213   std::string src_file =
214       "EMMC:" + old_file + ":" + std::to_string(old_size) + ":" + old_sha1;
215   std::vector<std::string> sha1s;
216   ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s));
217 
218   // EMMC:old_file:(size-1):sha1:(size+1):sha1 should fail the check.
219   src_file = "EMMC:" + old_file + ":" + std::to_string(old_size - 1) + ":" + old_sha1 + ":" +
220              std::to_string(old_size + 1) + ":" + old_sha1;
221   ASSERT_EQ(1, applypatch_check(src_file.c_str(), sha1s));
222 
223   // EMMC:old_file:(size-1):sha1:size:sha1:(size+1):sha1 should pass the check.
224   src_file = "EMMC:" + old_file + ":" +
225              std::to_string(old_size - 1) + ":" + old_sha1 + ":" +
226              std::to_string(old_size) + ":" + old_sha1 + ":" +
227              std::to_string(old_size + 1) + ":" + old_sha1;
228   ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s));
229 
230   // EMMC:old_file:(size+1):sha1:(size-1):sha1:size:sha1 should pass the check.
231   src_file = "EMMC:" + old_file + ":" +
232              std::to_string(old_size + 1) + ":" + old_sha1 + ":" +
233              std::to_string(old_size - 1) + ":" + old_sha1 + ":" +
234              std::to_string(old_size) + ":" + old_sha1;
235   ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s));
236 
237   // EMMC:new_file:(size+1):old_sha1:(size-1):old_sha1:size:old_sha1:size:new_sha1
238   // should pass the check.
239   src_file = "EMMC:" + new_file + ":" +
240              std::to_string(old_size + 1) + ":" + old_sha1 + ":" +
241              std::to_string(old_size - 1) + ":" + old_sha1 + ":" +
242              std::to_string(old_size) + ":" + old_sha1 + ":" +
243              std::to_string(new_size) + ":" + new_sha1;
244   ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s));
245 }
246 
TEST_F(ApplyPatchCacheTest,CheckCacheCorruptedSingle)247 TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSingle) {
248   mangle_file(old_file);
249   std::vector<std::string> sha1s = { old_sha1 };
250   ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
251 }
252 
TEST_F(ApplyPatchCacheTest,CheckCacheCorruptedMultiple)253 TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedMultiple) {
254   mangle_file(old_file);
255   std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b };
256   ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
257 }
258 
TEST_F(ApplyPatchCacheTest,CheckCacheCorruptedFailure)259 TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedFailure) {
260   mangle_file(old_file);
261   std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b };
262   ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
263 }
264 
TEST_F(ApplyPatchCacheTest,CheckCacheMissingSingle)265 TEST_F(ApplyPatchCacheTest, CheckCacheMissingSingle) {
266   unlink(&old_file[0]);
267   std::vector<std::string> sha1s = { old_sha1 };
268   ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
269 }
270 
TEST_F(ApplyPatchCacheTest,CheckCacheMissingMultiple)271 TEST_F(ApplyPatchCacheTest, CheckCacheMissingMultiple) {
272   unlink(&old_file[0]);
273   std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b };
274   ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
275 }
276 
TEST_F(ApplyPatchCacheTest,CheckCacheMissingFailure)277 TEST_F(ApplyPatchCacheTest, CheckCacheMissingFailure) {
278   unlink(&old_file[0]);
279   std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b };
280   ASSERT_NE(0, applypatch_check(&old_file[0], sha1s));
281 }
282 
TEST(ApplyPatchModesTest,InvalidArgs)283 TEST(ApplyPatchModesTest, InvalidArgs) {
284   // At least two args (including the filename).
285   ASSERT_EQ(2, applypatch_modes(1, (const char* []){ "applypatch" }));
286 
287   // Unrecognized args.
288   ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" }));
289 }
290 
TEST(ApplyPatchModesTest,PatchModeEmmcTarget)291 TEST(ApplyPatchModesTest, PatchModeEmmcTarget) {
292   std::string boot_img = from_testdata_base("boot.img");
293   size_t boot_img_size;
294   std::string boot_img_sha1;
295   sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
296 
297   std::string recovery_img = from_testdata_base("recovery.img");
298   size_t size;
299   std::string recovery_img_sha1;
300   sha1sum(recovery_img, &recovery_img_sha1, &size);
301   std::string recovery_img_size = std::to_string(size);
302 
303   std::string bonus_file = from_testdata_base("bonus.file");
304 
305   // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
306   TemporaryFile tmp1;
307   std::string src_file =
308       "EMMC:" + boot_img + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
309   std::string tgt_file = "EMMC:" + std::string(tmp1.path);
310   std::string patch = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p");
311   std::vector<const char*> args = {
312     "applypatch",
313     "-b",
314     bonus_file.c_str(),
315     src_file.c_str(),
316     tgt_file.c_str(),
317     recovery_img_sha1.c_str(),
318     recovery_img_size.c_str(),
319     patch.c_str()
320   };
321   ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
322 
323   // applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
324   TemporaryFile tmp2;
325   patch = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p");
326   tgt_file = "EMMC:" + std::string(tmp2.path);
327   std::vector<const char*> args2 = {
328     "applypatch",
329     src_file.c_str(),
330     tgt_file.c_str(),
331     recovery_img_sha1.c_str(),
332     recovery_img_size.c_str(),
333     patch.c_str()
334   };
335   ASSERT_EQ(0, applypatch_modes(args2.size(), args2.data()));
336 
337   // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> \
338   //               <src-sha1-fake>:<patch1> <src-sha1>:<patch2>
339   TemporaryFile tmp3;
340   tgt_file = "EMMC:" + std::string(tmp3.path);
341   std::string bad_sha1_a = android::base::StringPrintf("%040x", rand());
342   std::string bad_sha1_b = android::base::StringPrintf("%040x", rand());
343   std::string patch1 = bad_sha1_a + ":" + from_testdata_base("recovery-from-boot.p");
344   std::string patch2 = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p");
345   std::string patch3 = bad_sha1_b + ":" + from_testdata_base("recovery-from-boot.p");
346   std::vector<const char*> args3 = {
347     "applypatch",
348     "-b",
349     bonus_file.c_str(),
350     src_file.c_str(),
351     tgt_file.c_str(),
352     recovery_img_sha1.c_str(),
353     recovery_img_size.c_str(),
354     patch1.c_str(),
355     patch2.c_str(),
356     patch3.c_str()
357   };
358   ASSERT_EQ(0, applypatch_modes(args3.size(), args3.data()));
359 }
360 
TEST(ApplyPatchModesTest,PatchModeInvalidArgs)361 TEST(ApplyPatchModesTest, PatchModeInvalidArgs) {
362   // Invalid bonus file.
363   ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" }));
364 
365   std::string bonus_file = from_testdata_base("bonus.file");
366   // With bonus file, but missing args.
367   ASSERT_EQ(2, applypatch_modes(3, (const char* []){ "applypatch", "-b", bonus_file.c_str() }));
368 
369   std::string boot_img = from_testdata_base("boot.img");
370   size_t boot_img_size;
371   std::string boot_img_sha1;
372   sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
373 
374   std::string recovery_img = from_testdata_base("recovery.img");
375   size_t size;
376   std::string recovery_img_sha1;
377   sha1sum(recovery_img, &recovery_img_sha1, &size);
378   std::string recovery_img_size = std::to_string(size);
379 
380   // Bonus file is not supported in flash mode.
381   // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size>
382   TemporaryFile tmp4;
383   std::vector<const char*> args4 = {
384     "applypatch",
385     "-b",
386     bonus_file.c_str(),
387     boot_img.c_str(),
388     tmp4.path,
389     recovery_img_sha1.c_str(),
390     recovery_img_size.c_str()
391   };
392   ASSERT_NE(0, applypatch_modes(args4.size(), args4.data()));
393 
394   // Failed to parse patch args.
395   TemporaryFile tmp5;
396   std::string bad_arg1 =
397       "invalid-sha1:filename" + from_testdata_base("recovery-from-boot-with-bonus.p");
398   std::vector<const char*> args5 = {
399     "applypatch",
400     boot_img.c_str(),
401     tmp5.path,
402     recovery_img_sha1.c_str(),
403     recovery_img_size.c_str(),
404     bad_arg1.c_str()
405   };
406   ASSERT_NE(0, applypatch_modes(args5.size(), args5.data()));
407 
408   // Target size cannot be zero.
409   TemporaryFile tmp6;
410   std::string patch = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p");
411   std::vector<const char*> args6 = {
412     "applypatch",
413     boot_img.c_str(),
414     tmp6.path,
415     recovery_img_sha1.c_str(),
416     "0",  // target size
417     patch.c_str()
418   };
419   ASSERT_NE(0, applypatch_modes(args6.size(), args6.data()));
420 }
421 
TEST(ApplyPatchModesTest,CheckModeInvalidArgs)422 TEST(ApplyPatchModesTest, CheckModeInvalidArgs) {
423   // Insufficient args.
424   ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" }));
425 }
426 
TEST(ApplyPatchModesTest,SpaceModeInvalidArgs)427 TEST(ApplyPatchModesTest, SpaceModeInvalidArgs) {
428   // Insufficient args.
429   ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-s" }));
430 
431   // Invalid bytes arg.
432   ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "x" }));
433 
434   // 0 is invalid.
435   ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0" }));
436 
437   // 0x10 is fine.
438   ASSERT_EQ(0, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0x10" }));
439 }
440 
TEST(ApplyPatchModesTest,ShowLicenses)441 TEST(ApplyPatchModesTest, ShowLicenses) {
442   ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" }));
443 }
444