1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdint.h>
31 #include <stdio.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <chrono>
37 #include <cstdlib>
38 #include <fstream>
39 #include <map>
40 #include <random>
41 #include <regex>
42 #include <set>
43 #include <thread>
44 #include <vector>
45
46 #include <android-base/file.h>
47 #include <android-base/parseint.h>
48 #include <android-base/stringprintf.h>
49 #include <android-base/strings.h>
50 #include <gtest/gtest.h>
51 #include <sparse/sparse.h>
52
53 #include "constants.h"
54 #include "fastboot_driver.h"
55 #include "usb.h"
56
57 #include "extensions.h"
58 #include "fixtures.h"
59 #include "test_utils.h"
60 #include "transport_sniffer.h"
61
62 namespace fastboot {
63
64 extension::Configuration config; // The parsed XML config
65
66 std::string SEARCH_PATH;
67 std::string OUTPUT_PATH;
68
69 // gtest's INSTANTIATE_TEST_CASE_P() must be at global scope,
70 // so our autogenerated tests must be as well
71 std::vector<std::pair<std::string, extension::Configuration::GetVar>> GETVAR_XML_TESTS;
72 std::vector<std::tuple<std::string, bool, extension::Configuration::CommandTest>> OEM_XML_TESTS;
73 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>> PARTITION_XML_TESTS;
74 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
75 PARTITION_XML_WRITEABLE;
76 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
77 PARTITION_XML_WRITE_HASHABLE;
78 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
79 PARTITION_XML_WRITE_PARSED;
80 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
81 PARTITION_XML_WRITE_HASH_NONPARSED;
82 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
83 PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE;
84 std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>>
85 PACKED_XML_SUCCESS_TESTS;
86 std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>> PACKED_XML_FAIL_TESTS;
87 // This only has 1 or zero elements so it will disappear from gtest when empty
88 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
89 SINGLE_PARTITION_XML_WRITE_HASHABLE;
90
91 const std::string DEFAULT_OUPUT_NAME = "out.img";
92 // const char scratch_partition[] = "userdata";
93 const std::vector<std::string> CMDS{"boot", "continue", "download:", "erase:", "flash:",
94 "getvar:", "reboot", "set_active:", "upload"};
95
96 // For pretty printing we need all these overloads
operator <<(::std::ostream & os,const RetCode & ret)97 ::std::ostream& operator<<(::std::ostream& os, const RetCode& ret) {
98 return os << FastBootDriver::RCString(ret);
99 }
100
PartitionHash(FastBootDriver * fb,const std::string & part,std::string * hash,int * retcode,std::string * err_msg)101 bool PartitionHash(FastBootDriver* fb, const std::string& part, std::string* hash, int* retcode,
102 std::string* err_msg) {
103 if (config.checksum.empty()) {
104 return -1;
105 }
106
107 std::string resp;
108 std::vector<std::string> info;
109 const std::string cmd = config.checksum + ' ' + part;
110 RetCode ret;
111 if ((ret = fb->RawCommand(cmd, &resp, &info)) != SUCCESS) {
112 *err_msg =
113 android::base::StringPrintf("Hashing partition with command '%s' failed with: %s",
114 cmd.c_str(), fb->RCString(ret).c_str());
115 return false;
116 }
117 std::stringstream imploded;
118 std::copy(info.begin(), info.end(), std::ostream_iterator<std::string>(imploded, "\n"));
119
120 // If payload, we validate that as well
121 const std::vector<std::string> args = SplitBySpace(config.checksum_parser);
122 std::vector<std::string> prog_args(args.begin() + 1, args.end());
123 prog_args.push_back(resp); // Pass in the full command
124 prog_args.push_back(SEARCH_PATH + imploded.str()); // Pass in the save location
125
126 int pipe;
127 pid_t pid = StartProgram(args[0], prog_args, &pipe);
128 if (pid <= 0) {
129 *err_msg = android::base::StringPrintf("Launching hash parser '%s' failed with: %s",
130 config.checksum_parser.c_str(), strerror(errno));
131 return false;
132 }
133 *retcode = WaitProgram(pid, pipe, hash);
134 if (*retcode) {
135 // In this case the stderr pipe is a log message
136 *err_msg = android::base::StringPrintf("Hash parser '%s' failed with: %s",
137 config.checksum_parser.c_str(), hash->c_str());
138 return false;
139 }
140
141 return true;
142 }
143
SparseToBuf(sparse_file * sf,std::vector<char> * out,bool with_crc=false)144 bool SparseToBuf(sparse_file* sf, std::vector<char>* out, bool with_crc = false) {
145 int64_t len = sparse_file_len(sf, true, with_crc);
146 if (len <= 0) {
147 return false;
148 }
149 out->clear();
150 auto cb = [](void* priv, const void* data, size_t len) {
151 auto vec = static_cast<std::vector<char>*>(priv);
152 const char* cbuf = static_cast<const char*>(data);
153 vec->insert(vec->end(), cbuf, cbuf + len);
154 return 0;
155 };
156
157 return !sparse_file_callback(sf, true, with_crc, cb, out);
158 }
159
160 // Only allow alphanumeric, _, -, and .
__anona0fc38ea0202(char c) 161 const auto not_allowed = [](char c) -> int {
162 return !(isalnum(c) || c == '_' || c == '-' || c == '.');
163 };
164
165 // Test that USB even works
TEST(USBFunctionality,USBConnect)166 TEST(USBFunctionality, USBConnect) {
167 const auto matcher = [](usb_ifc_info* info) -> int {
168 return FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
169 };
170 std::unique_ptr<Transport> transport;
171 for (int i = 0; i < FastBootTest::MAX_USB_TRIES && !transport; i++) {
172 transport = usb_open(matcher);
173 std::this_thread::sleep_for(std::chrono::milliseconds(10));
174 }
175 ASSERT_NE(transport.get(), nullptr) << "Could not find the fastboot device after: "
176 << 10 * FastBootTest::MAX_USB_TRIES << "ms";
177 if (transport) {
178 transport->Close();
179 }
180 }
181
182 // Test commands related to super partition
TEST_F(LogicalPartitionCompliance,SuperPartition)183 TEST_F(LogicalPartitionCompliance, SuperPartition) {
184 ASSERT_TRUE(UserSpaceFastboot());
185 std::string partition_type;
186 // getvar partition-type:super must fail for retrofit devices because the
187 // partition does not exist.
188 if (fb->GetVar("partition-type:super", &partition_type) == SUCCESS) {
189 std::string is_logical;
190 EXPECT_EQ(fb->GetVar("is-logical:super", &is_logical), SUCCESS)
191 << "getvar is-logical:super failed";
192 EXPECT_EQ(is_logical, "no") << "super must not be a logical partition";
193 std::string super_name;
194 EXPECT_EQ(fb->GetVar("super-partition-name", &super_name), SUCCESS)
195 << "'getvar super-partition-name' failed";
196 EXPECT_EQ(super_name, "super") << "'getvar super-partition-name' must return 'super' for "
197 "device with a super partition";
198 }
199 }
200
201 // Test 'fastboot getvar is-logical'
TEST_F(LogicalPartitionCompliance,GetVarIsLogical)202 TEST_F(LogicalPartitionCompliance, GetVarIsLogical) {
203 ASSERT_TRUE(UserSpaceFastboot());
204 std::string has_slot;
205 EXPECT_EQ(fb->GetVar("has-slot:system", &has_slot), SUCCESS) << "getvar has-slot:system failed";
206 std::string is_logical_cmd_system = "is-logical:system";
207 std::string is_logical_cmd_vendor = "is-logical:vendor";
208 std::string is_logical_cmd_boot = "is-logical:boot";
209 if (has_slot == "yes") {
210 std::string current_slot;
211 ASSERT_EQ(fb->GetVar("current-slot", ¤t_slot), SUCCESS)
212 << "getvar current-slot failed";
213 std::string slot_suffix = "_" + current_slot;
214 is_logical_cmd_system += slot_suffix;
215 is_logical_cmd_vendor += slot_suffix;
216 is_logical_cmd_boot += slot_suffix;
217 }
218 std::string is_logical;
219 EXPECT_EQ(fb->GetVar(is_logical_cmd_system, &is_logical), SUCCESS)
220 << "system must be a logical partition";
221 EXPECT_EQ(is_logical, "yes");
222 EXPECT_EQ(fb->GetVar(is_logical_cmd_vendor, &is_logical), SUCCESS)
223 << "vendor must be a logical partition";
224 EXPECT_EQ(is_logical, "yes");
225 EXPECT_EQ(fb->GetVar(is_logical_cmd_boot, &is_logical), SUCCESS)
226 << "boot must not be logical partition";
227 EXPECT_EQ(is_logical, "no");
228 }
229
TEST_F(LogicalPartitionCompliance,FastbootRebootTest)230 TEST_F(LogicalPartitionCompliance, FastbootRebootTest) {
231 ASSERT_TRUE(UserSpaceFastboot());
232 GTEST_LOG_(INFO) << "Rebooting back to fastbootd mode";
233 fb->RebootTo("fastboot");
234
235 ReconnectFastbootDevice();
236 ASSERT_TRUE(UserSpaceFastboot());
237 }
238
239 // Testing creation/resize/delete of logical partitions
TEST_F(LogicalPartitionCompliance,CreateResizeDeleteLP)240 TEST_F(LogicalPartitionCompliance, CreateResizeDeleteLP) {
241 ASSERT_TRUE(UserSpaceFastboot());
242 std::string test_partition_name = "test_partition";
243 std::string slot_count;
244 // Add suffix to test_partition_name if device is slotted.
245 EXPECT_EQ(fb->GetVar("slot-count", &slot_count), SUCCESS) << "getvar slot-count failed";
246 int32_t num_slots = strtol(slot_count.c_str(), nullptr, 10);
247 if (num_slots > 0) {
248 std::string current_slot;
249 EXPECT_EQ(fb->GetVar("current-slot", ¤t_slot), SUCCESS)
250 << "getvar current-slot failed";
251 std::string slot_suffix = "_" + current_slot;
252 test_partition_name += slot_suffix;
253 }
254
255 GTEST_LOG_(INFO) << "Testing 'fastboot create-logical-partition' command";
256 EXPECT_EQ(fb->CreatePartition(test_partition_name, "0"), SUCCESS)
257 << "create-logical-partition failed";
258 GTEST_LOG_(INFO) << "Testing 'fastboot resize-logical-partition' command";
259 EXPECT_EQ(fb->ResizePartition(test_partition_name, "4096"), SUCCESS)
260 << "resize-logical-partition failed";
261 std::vector<char> buf(4096);
262
263 GTEST_LOG_(INFO) << "Flashing a logical partition..";
264 EXPECT_EQ(fb->FlashPartition(test_partition_name, buf), SUCCESS)
265 << "flash logical -partition failed";
266
267 GTEST_LOG_(INFO) << "Testing 'fastboot delete-logical-partition' command";
268 EXPECT_EQ(fb->DeletePartition(test_partition_name), SUCCESS)
269 << "delete logical-partition failed";
270 }
271
272 // Conformance tests
TEST_F(Conformance,GetVar)273 TEST_F(Conformance, GetVar) {
274 std::string product;
275 EXPECT_EQ(fb->GetVar("product", &product), SUCCESS) << "getvar:product failed";
276 EXPECT_NE(product, "") << "getvar:product response was empty string";
277 EXPECT_EQ(std::count_if(product.begin(), product.end(), not_allowed), 0)
278 << "getvar:product response contained illegal chars";
279 EXPECT_LE(product.size(), FB_RESPONSE_SZ - 4) << "getvar:product response was too large";
280 }
281
TEST_F(Conformance,GetVarVersionBootloader)282 TEST_F(Conformance, GetVarVersionBootloader) {
283 std::string var;
284 EXPECT_EQ(fb->GetVar("version-bootloader", &var), SUCCESS)
285 << "getvar:version-bootloader failed";
286 EXPECT_NE(var, "") << "getvar:version-bootloader response was empty string";
287 EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
288 << "getvar:version-bootloader response contained illegal chars";
289 EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:version-bootloader response was too large";
290 }
291
TEST_F(Conformance,GetVarVersionBaseband)292 TEST_F(Conformance, GetVarVersionBaseband) {
293 std::string var;
294 EXPECT_EQ(fb->GetVar("version-baseband", &var), SUCCESS) << "getvar:version-baseband failed";
295 EXPECT_NE(var, "") << "getvar:version-baseband response was empty string";
296 EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
297 << "getvar:version-baseband response contained illegal chars";
298 EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:version-baseband response was too large";
299 }
300
TEST_F(Conformance,GetVarSerialNo)301 TEST_F(Conformance, GetVarSerialNo) {
302 std::string var;
303 EXPECT_EQ(fb->GetVar("serialno", &var), SUCCESS) << "getvar:serialno failed";
304 EXPECT_NE(var, "") << "getvar:serialno can not be empty string";
305 EXPECT_EQ(std::count_if(var.begin(), var.end(), isalnum), var.size())
306 << "getvar:serialno must be alpha-numeric";
307 EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:serialno response is too long";
308 }
309
TEST_F(Conformance,GetVarSecure)310 TEST_F(Conformance, GetVarSecure) {
311 std::string var;
312 EXPECT_EQ(fb->GetVar("secure", &var), SUCCESS);
313 EXPECT_TRUE(var == "yes" || var == "no");
314 }
315
TEST_F(Conformance,GetVarOffModeCharge)316 TEST_F(Conformance, GetVarOffModeCharge) {
317 std::string var;
318 EXPECT_EQ(fb->GetVar("off-mode-charge", &var), SUCCESS) << "getvar:off-mode-charge failed";
319 EXPECT_TRUE(var == "0" || var == "1") << "getvar:off-mode-charge response must be '0' or '1'";
320 }
321
TEST_F(Conformance,GetVarVariant)322 TEST_F(Conformance, GetVarVariant) {
323 std::string var;
324 EXPECT_EQ(fb->GetVar("variant", &var), SUCCESS) << "getvar:variant failed";
325 EXPECT_NE(var, "") << "getvar:variant response can not be empty";
326 EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:variant response is too large";
327 }
328
TEST_F(Conformance,GetVarRevision)329 TEST_F(Conformance, GetVarRevision) {
330 std::string var;
331 EXPECT_EQ(fb->GetVar("hw-revision", &var), SUCCESS) << "getvar:hw-revision failed";
332 EXPECT_NE(var, "") << "getvar:battery-voltage response was empty";
333 EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
334 << "getvar:hw-revision contained illegal ASCII chars";
335 EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << "getvar:hw-revision response was too large";
336 }
337
TEST_F(Conformance,GetVarBattVoltage)338 TEST_F(Conformance, GetVarBattVoltage) {
339 std::string var;
340 EXPECT_EQ(fb->GetVar("battery-voltage", &var), SUCCESS) << "getvar:battery-voltage failed";
341 EXPECT_NE(var, "") << "getvar:battery-voltage response was empty";
342 EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)
343 << "getvar:battery-voltage response contains illegal ASCII chars";
344 EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4)
345 << "getvar:battery-voltage response is too large: " + var;
346 }
347
TEST_F(Conformance,GetVarBattVoltageOk)348 TEST_F(Conformance, GetVarBattVoltageOk) {
349 std::string var;
350 EXPECT_EQ(fb->GetVar("battery-soc-ok", &var), SUCCESS) << "getvar:battery-soc-ok failed";
351 EXPECT_TRUE(var == "yes" || var == "no") << "getvar:battery-soc-ok must be 'yes' or 'no'";
352 }
353
AssertHexUint32(const std::string & name,const std::string & var)354 void AssertHexUint32(const std::string& name, const std::string& var) {
355 ASSERT_NE(var, "") << "getvar:" << name << " responded with empty string";
356 // This must start with 0x
357 ASSERT_FALSE(isspace(var.front()))
358 << "getvar:" << name << " responded with a string with leading whitespace";
359 ASSERT_FALSE(var.compare(0, 2, "0x"))
360 << "getvar:" << name << " responded with a string that does not start with 0x...";
361 int64_t size = strtoll(var.c_str(), nullptr, 16);
362 ASSERT_GT(size, 0) << "'" + var + "' is not a valid response from getvar:" << name;
363 // At most 32-bits
364 ASSERT_LE(size, std::numeric_limits<uint32_t>::max())
365 << "getvar:" << name << " must fit in a uint32_t";
366 ASSERT_LE(var.size(), FB_RESPONSE_SZ - 4)
367 << "getvar:" << name << " responded with too large of string: " + var;
368 }
369
TEST_F(Conformance,GetVarDownloadSize)370 TEST_F(Conformance, GetVarDownloadSize) {
371 std::string var;
372 EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
373 AssertHexUint32("max-download-size", var);
374 }
375
376 // If fetch is supported, getvar:max-fetch-size must return a hex string.
TEST_F(Conformance,GetVarFetchSize)377 TEST_F(Conformance, GetVarFetchSize) {
378 std::string var;
379 if (SUCCESS != fb->GetVar("max-fetch-size", &var)) {
380 GTEST_SKIP() << "getvar:max-fetch-size failed";
381 }
382 AssertHexUint32("max-fetch-size", var);
383 }
384
TEST_F(Conformance,GetVarAll)385 TEST_F(Conformance, GetVarAll) {
386 std::vector<std::string> vars;
387 EXPECT_EQ(fb->GetVarAll(&vars), SUCCESS) << "getvar:all failed";
388 EXPECT_GT(vars.size(), 0) << "getvar:all did not respond with any INFO responses";
389 for (const auto& s : vars) {
390 EXPECT_LE(s.size(), FB_RESPONSE_SZ - 4)
391 << "getvar:all included an INFO response: 'INFO" + s << "' which is too long";
392 }
393 }
394
TEST_F(Conformance,UnlockAbility)395 TEST_F(Conformance, UnlockAbility) {
396 std::string resp;
397 std::vector<std::string> info;
398 // Userspace fastboot implementations do not have a way to get this
399 // information.
400 if (UserSpaceFastboot()) {
401 GTEST_LOG_(INFO) << "This test is skipped for userspace fastboot.";
402 return;
403 }
404 EXPECT_EQ(fb->RawCommand("flashing get_unlock_ability", &resp, &info), SUCCESS)
405 << "'flashing get_unlock_ability' failed";
406 // There are two ways this can be reported, through info or the actual response
407 char last;
408 if (!resp.empty()) { // must be in the response
409 last = resp.back();
410 } else { // else must be in info
411 ASSERT_FALSE(info.empty()) << "'flashing get_unlock_ability' returned empty response";
412 ASSERT_FALSE(info.back().empty()) << "Expected non-empty info response";
413 last = info.back().back();
414 }
415 ASSERT_TRUE(last == '1' || last == '0') << "Unlock ability must report '0' or '1' in response";
416 }
417
TEST_F(Conformance,PartitionInfo)418 TEST_F(Conformance, PartitionInfo) {
419 std::vector<std::tuple<std::string, uint64_t>> parts;
420 EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
421 EXPECT_GT(parts.size(), 0)
422 << "getvar:all did not report any partition-size: through INFO responses";
423 std::set<std::string> allowed{"ext4", "f2fs", "raw"};
424 for (const auto& p : parts) {
425 EXPECT_GE(std::get<1>(p), 0);
426 std::string part(std::get<0>(p));
427 std::set<std::string> allowed{"ext4", "f2fs", "raw"};
428 std::string resp;
429 EXPECT_EQ(fb->GetVar("partition-type:" + part, &resp), SUCCESS);
430 EXPECT_NE(allowed.find(resp), allowed.end()) << "getvar:partition-type:" + part << " was '"
431 << resp << "' this is not a valid type";
432 const std::string cmd = "partition-size:" + part;
433 EXPECT_EQ(fb->GetVar(cmd, &resp), SUCCESS);
434
435 // This must start with 0x
436 EXPECT_FALSE(isspace(resp.front()))
437 << cmd + " responded with a string with leading whitespace";
438 EXPECT_FALSE(resp.compare(0, 2, "0x"))
439 << cmd + "responded with a string that does not start with 0x...";
440 uint64_t size;
441 ASSERT_TRUE(android::base::ParseUint(resp, &size))
442 << "'" + resp + "' is not a valid response from " + cmd;
443 }
444 }
445
TEST_F(Conformance,Slots)446 TEST_F(Conformance, Slots) {
447 std::string var;
448 ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "getvar:slot-count failed";
449 ASSERT_EQ(std::count_if(var.begin(), var.end(), isdigit), var.size())
450 << "'" << var << "' is not all digits which it should be for getvar:slot-count";
451 int32_t num_slots = strtol(var.c_str(), nullptr, 10);
452
453 // Can't run out of alphabet letters...
454 ASSERT_LE(num_slots, 26) << "What?! You can't have more than 26 slots";
455
456 std::vector<std::tuple<std::string, uint64_t>> parts;
457 EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
458
459 std::map<std::string, std::set<char>> part_slots;
460 if (num_slots > 0) {
461 EXPECT_EQ(fb->GetVar("current-slot", &var), SUCCESS) << "getvar:current-slot failed";
462
463 for (const auto& p : parts) {
464 std::string part(std::get<0>(p));
465 std::regex reg("([[:graph:]]*)_([[:lower:]])");
466 std::smatch sm;
467
468 if (std::regex_match(part, sm, reg)) { // This partition has slots
469 std::string part_base(sm[1]);
470 std::string slot(sm[2]);
471 EXPECT_EQ(fb->GetVar("has-slot:" + part_base, &var), SUCCESS)
472 << "'getvar:has-slot:" << part_base << "' failed";
473 EXPECT_EQ(var, "yes") << "'getvar:has-slot:" << part_base << "' was not 'yes'";
474 EXPECT_TRUE(islower(slot.front()))
475 << "'" << slot.front() << "' is an invalid slot-suffix for " << part_base;
476 std::set<char> tmp{slot.front()};
477 part_slots.emplace(part_base, tmp);
478 part_slots.at(part_base).insert(slot.front());
479 } else {
480 EXPECT_EQ(fb->GetVar("has-slot:" + part, &var), SUCCESS)
481 << "'getvar:has-slot:" << part << "' failed";
482 EXPECT_EQ(var, "no") << "'getvar:has-slot:" << part << "' should be no";
483 }
484 }
485 // Ensure each partition has the correct slot suffix
486 for (const auto& iter : part_slots) {
487 const std::set<char>& char_set = iter.second;
488 std::string chars;
489 for (char c : char_set) {
490 chars += c;
491 chars += ',';
492 }
493 EXPECT_EQ(char_set.size(), num_slots)
494 << "There should only be slot suffixes from a to " << 'a' + num_slots - 1
495 << " instead encountered: " << chars;
496 for (const char c : char_set) {
497 EXPECT_GE(c, 'a') << "Encountered invalid slot suffix of '" << c << "'";
498 EXPECT_LT(c, 'a' + num_slots) << "Encountered invalid slot suffix of '" << c << "'";
499 }
500 }
501 }
502 }
503
TEST_F(Conformance,SetActive)504 TEST_F(Conformance, SetActive) {
505 std::string var;
506 ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "getvar:slot-count failed";
507 ASSERT_EQ(std::count_if(var.begin(), var.end(), isdigit), var.size())
508 << "'" << var << "' is not all digits which it should be for getvar:slot-count";
509 int32_t num_slots = strtol(var.c_str(), nullptr, 10);
510
511 // Can't run out of alphabet letters...
512 ASSERT_LE(num_slots, 26) << "You can't have more than 26 slots";
513
514 for (char c = 'a'; c < 'a' + num_slots; c++) {
515 const std::string slot(&c, &c + 1);
516 ASSERT_EQ(fb->SetActive(slot), SUCCESS) << "Set active for slot '" << c << "' failed";
517 ASSERT_EQ(fb->GetVar("current-slot", &var), SUCCESS) << "getvar:current-slot failed";
518 EXPECT_EQ(var, slot) << "getvar:current-slot repots incorrect slot after setting it";
519 }
520 }
521
TEST_F(Conformance,LockAndUnlockPrompt)522 TEST_F(Conformance, LockAndUnlockPrompt) {
523 std::string resp;
524 ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
525 ASSERT_TRUE(resp == "yes" || resp == "no")
526 << "Device did not respond with 'yes' or 'no' for getvar:unlocked";
527 bool curr = resp == "yes";
528 if (UserSpaceFastboot()) {
529 GTEST_LOG_(INFO) << "This test is skipped for userspace fastboot.";
530 return;
531 }
532
533 for (int i = 0; i < 2; i++) {
534 std::string action = !curr ? "unlock" : "lock";
535 printf("Device should prompt to '%s' bootloader, select 'no'\n", action.c_str());
536 SetLockState(!curr, false);
537 ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
538 ASSERT_EQ(resp, curr ? "yes" : "no") << "The locked/unlocked state of the bootloader "
539 "incorrectly changed after selecting no";
540 printf("Device should prompt to '%s' bootloader, select 'yes'\n", action.c_str());
541 SetLockState(!curr, true);
542 ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
543 ASSERT_EQ(resp, !curr ? "yes" : "no") << "The locked/unlocked state of the bootloader "
544 "failed to change after selecting yes";
545 curr = !curr;
546 }
547 }
548
TEST_F(Conformance,SparseBlockSupport0)549 TEST_F(Conformance, SparseBlockSupport0) {
550 // The sparse block size can be any multiple of 4
551 std::string var;
552 EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
553 int64_t size = strtoll(var.c_str(), nullptr, 16);
554
555 // It is reasonable to expect it to handle a single dont care block equal to its DL size
556 for (int64_t bs = 4; bs < size; bs <<= 1) {
557 SparseWrapper sparse(bs, bs);
558 ASSERT_TRUE(*sparse) << "Sparse file creation failed on: " << bs;
559 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
560 EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
561 }
562 }
563
TEST_F(Conformance,SparseBlockSupport1)564 TEST_F(Conformance, SparseBlockSupport1) {
565 // The sparse block size can be any multiple of 4
566 std::string var;
567 EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
568 int64_t size = strtoll(var.c_str(), nullptr, 16);
569
570 // handle a packed block to half its max download size block
571 for (int64_t bs = 4; bs < size / 2; bs <<= 1) {
572 SparseWrapper sparse(bs, bs);
573 ASSERT_TRUE(*sparse) << "Sparse file creation failed on: " << bs;
574 std::vector<char> buf = RandomBuf(bs);
575 ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
576 << "Adding data failed to sparse file: " << sparse.Rep();
577 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
578 EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
579 }
580 }
581
582 // A single don't care download
TEST_F(Conformance,SparseDownload0)583 TEST_F(Conformance, SparseDownload0) {
584 SparseWrapper sparse(4096, 4096);
585 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
586 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
587 EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
588 }
589
TEST_F(Conformance,SparseDownload1)590 TEST_F(Conformance, SparseDownload1) {
591 SparseWrapper sparse(4096, 10 * 4096);
592 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
593 std::vector<char> buf = RandomBuf(4096);
594 ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 9), 0)
595 << "Adding data failed to sparse file: " << sparse.Rep();
596 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
597 EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
598 }
599
TEST_F(Conformance,SparseDownload2)600 TEST_F(Conformance, SparseDownload2) {
601 SparseWrapper sparse(4096, 4097);
602 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
603 std::vector<char> buf = RandomBuf(4096);
604 ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
605 << "Adding data failed to sparse file: " << sparse.Rep();
606 std::vector<char> buf2 = RandomBuf(1);
607 ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 1), 0)
608 << "Adding data failed to sparse file: " << sparse.Rep();
609 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
610 EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
611 }
612
TEST_F(Conformance,SparseDownload3)613 TEST_F(Conformance, SparseDownload3) {
614 std::string var;
615 EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
616 int size = strtoll(var.c_str(), nullptr, 16);
617
618 SparseWrapper sparse(4096, size);
619 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
620 // Don't want this to take forever
621 unsigned num_chunks = std::min(1000, size / (2 * 4096));
622 for (int i = 0; i < num_chunks; i++) {
623 std::vector<char> buf;
624 int r = random_int(0, 2);
625 // Three cases
626 switch (r) {
627 case 0:
628 break; // Dont Care chunnk
629 case 1: // Buffer
630 buf = RandomBuf(4096);
631 ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), i), 0)
632 << "Adding data failed to sparse file: " << sparse.Rep();
633 break;
634 case 2: // fill
635 ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, i), 0)
636 << "Adding fill to sparse file failed: " << sparse.Rep();
637 break;
638 }
639 }
640 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
641 EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
642 }
643
TEST_F(Conformance,SparseVersionCheck)644 TEST_F(Conformance, SparseVersionCheck) {
645 SparseWrapper sparse(4096, 4096);
646 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
647 std::vector<char> buf;
648 ASSERT_TRUE(SparseToBuf(*sparse, &buf)) << "Sparse buffer creation failed";
649 // Invalid, right after magic
650 buf[4] = 0xff;
651 ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
652 ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
653
654 // It can either reject this download or reject it during flash
655 if (HandleResponse() != DEVICE_FAIL) {
656 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
657 << "Flashing an invalid sparse version should fail " << sparse.Rep();
658 }
659 }
660
TEST_F(UnlockPermissions,Download)661 TEST_F(UnlockPermissions, Download) {
662 std::vector<char> buf{'a', 'o', 's', 'p'};
663 EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download 4-byte payload failed";
664 }
665
TEST_F(UnlockPermissions,DownloadFlash)666 TEST_F(UnlockPermissions, DownloadFlash) {
667 std::vector<char> buf{'a', 'o', 's', 'p'};
668 EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download failed in unlocked mode";
669 ;
670 std::vector<std::tuple<std::string, uint64_t>> parts;
671 EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in unlocked mode";
672 }
673
674 // If the implementation supports getvar:max-fetch-size, it must also support fetch:vendor_boot*.
TEST_F(UnlockPermissions,FetchVendorBoot)675 TEST_F(UnlockPermissions, FetchVendorBoot) {
676 std::string var;
677 uint64_t fetch_size;
678 if (fb->GetVar("max-fetch-size", &var) != SUCCESS) {
679 GTEST_SKIP() << "This test is skipped because fetch is not supported.";
680 }
681 ASSERT_FALSE(var.empty());
682 ASSERT_TRUE(android::base::ParseUint(var, &fetch_size)) << var << " is not an integer";
683 std::vector<std::tuple<std::string, uint64_t>> parts;
684 EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
685 for (const auto& [partition, partition_size] : parts) {
686 if (!android::base::StartsWith(partition, "vendor_boot")) continue;
687 TemporaryFile fetched;
688
689 uint64_t offset = 0;
690 while (offset < partition_size) {
691 uint64_t chunk_size = std::min(fetch_size, partition_size - offset);
692 auto ret = fb->FetchToFd(partition, fetched.fd, offset, chunk_size);
693 ASSERT_EQ(fastboot::RetCode::SUCCESS, ret)
694 << "Unable to fetch " << partition << " (offset=" << offset
695 << ", size=" << chunk_size << ")";
696 offset += chunk_size;
697 }
698 }
699 }
700
TEST_F(LockPermissions,DownloadFlash)701 TEST_F(LockPermissions, DownloadFlash) {
702 std::vector<char> buf{'a', 'o', 's', 'p'};
703 EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download failed in locked mode";
704 std::vector<std::tuple<std::string, uint64_t>> parts;
705 EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed in locked mode";
706 std::string resp;
707 for (const auto& tup : parts) {
708 EXPECT_EQ(fb->Flash(std::get<0>(tup), &resp), DEVICE_FAIL)
709 << "Device did not respond with FAIL when trying to flash '" << std::get<0>(tup)
710 << "' in locked mode";
711 EXPECT_GT(resp.size(), 0)
712 << "Device sent empty error message after FAIL"; // meaningful error message
713 }
714 }
715
TEST_F(LockPermissions,Erase)716 TEST_F(LockPermissions, Erase) {
717 std::vector<std::tuple<std::string, uint64_t>> parts;
718 EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
719 std::string resp;
720 for (const auto& tup : parts) {
721 EXPECT_EQ(fb->Erase(std::get<0>(tup), &resp), DEVICE_FAIL)
722 << "Device did not respond with FAIL when trying to erase '" << std::get<0>(tup)
723 << "' in locked mode";
724 EXPECT_GT(resp.size(), 0) << "Device sent empty error message after FAIL";
725 }
726 }
727
TEST_F(LockPermissions,SetActive)728 TEST_F(LockPermissions, SetActive) {
729 std::vector<std::tuple<std::string, uint64_t>> parts;
730 EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
731
732 std::string resp;
733 EXPECT_EQ(fb->GetVar("slot-count", &resp), SUCCESS) << "getvar:slot-count failed";
734 int32_t num_slots = strtol(resp.c_str(), nullptr, 10);
735
736 for (const auto& tup : parts) {
737 std::string part(std::get<0>(tup));
738 std::regex reg("([[:graph:]]*)_([[:lower:]])");
739 std::smatch sm;
740
741 if (std::regex_match(part, sm, reg)) { // This partition has slots
742 std::string part_base(sm[1]);
743 for (char c = 'a'; c < 'a' + num_slots; c++) {
744 // We should not be able to SetActive any of these
745 EXPECT_EQ(fb->SetActive(part_base + '_' + c, &resp), DEVICE_FAIL)
746 << "set:active:" << part_base + '_' + c << " did not fail in locked mode";
747 }
748 }
749 }
750 }
751
TEST_F(LockPermissions,Boot)752 TEST_F(LockPermissions, Boot) {
753 std::vector<char> buf;
754 buf.resize(1000);
755 EXPECT_EQ(fb->Download(buf), SUCCESS) << "A 1000 byte download failed";
756 std::string resp;
757 ASSERT_EQ(fb->Boot(&resp), DEVICE_FAIL)
758 << "The device did not respond with failure for 'boot' when locked";
759 EXPECT_GT(resp.size(), 0) << "No error message was returned by device after FAIL";
760 }
761
TEST_F(LockPermissions,FetchVendorBoot)762 TEST_F(LockPermissions, FetchVendorBoot) {
763 std::vector<std::tuple<std::string, uint64_t>> parts;
764 EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
765 for (const auto& [partition, _] : parts) {
766 TemporaryFile fetched;
767 ASSERT_EQ(fb->FetchToFd(partition, fetched.fd, 0, 0), DEVICE_FAIL)
768 << "fetch:" << partition << ":0:0 did not fail in locked mode";
769 }
770 }
771
TEST_F(Fuzz,DownloadSize)772 TEST_F(Fuzz, DownloadSize) {
773 std::string var;
774 EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "getvar:max-download-size failed";
775 int64_t size = strtoll(var.c_str(), nullptr, 0);
776 EXPECT_GT(size, 0) << '\'' << var << "' is not a valid response for getvar:max-download-size";
777
778 EXPECT_EQ(DownloadCommand(size + 1), DEVICE_FAIL)
779 << "Device reported max-download-size as '" << size
780 << "' but did not reject a download of " << size + 1;
781
782 std::vector<char> buf(size);
783 EXPECT_EQ(fb->Download(buf), SUCCESS) << "Device reported max-download-size as '" << size
784 << "' but downloading a payload of this size failed";
785 ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
786 }
787
TEST_F(Fuzz,DownloadPartialBuf)788 TEST_F(Fuzz, DownloadPartialBuf) {
789 std::vector<char> buf{'a', 'o', 's', 'p'};
790 ASSERT_EQ(DownloadCommand(buf.size() + 1), SUCCESS)
791 << "Download command for " << buf.size() + 1 << " bytes failed";
792
793 std::string resp;
794 RetCode ret = SendBuffer(buf);
795 EXPECT_EQ(ret, SUCCESS) << "Device did not accept partial payload download";
796 // Send the partial buffer, then cancel it with a reset
797 EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
798
799 ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
800 // The device better still work after all that if we unplug and replug
801 EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "getvar:product failed";
802 }
803
TEST_F(Fuzz,DownloadOverRun)804 TEST_F(Fuzz, DownloadOverRun) {
805 std::vector<char> buf(1000, 'F');
806 ASSERT_EQ(DownloadCommand(10), SUCCESS) << "Device rejected download request for 10 bytes";
807 // There are two ways to handle this
808 // Accept download, but send error response
809 // Reject the download outright
810 std::string resp;
811 RetCode ret = SendBuffer(buf);
812 if (ret == SUCCESS) {
813 // If it accepts the buffer, it better send back an error response
814 EXPECT_EQ(HandleResponse(&resp), DEVICE_FAIL)
815 << "After sending too large of a payload for a download command, device accepted "
816 "payload and did not respond with FAIL";
817 } else {
818 EXPECT_EQ(ret, IO_ERROR) << "After sending too large of a payload for a download command, "
819 "device did not return error";
820 }
821
822 ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
823 // The device better still work after all that if we unplug and replug
824 EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
825 EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
826 << "Device did not respond with SUCCESS to getvar:product.";
827 }
828
TEST_F(Fuzz,DownloadInvalid1)829 TEST_F(Fuzz, DownloadInvalid1) {
830 EXPECT_EQ(DownloadCommand(0), DEVICE_FAIL)
831 << "Device did not respond with FAIL for malformed download command 'download:0'";
832 }
833
TEST_F(Fuzz,DownloadInvalid2)834 TEST_F(Fuzz, DownloadInvalid2) {
835 std::string cmd("download:1");
836 EXPECT_EQ(fb->RawCommand("download:1"), DEVICE_FAIL)
837 << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
838 }
839
TEST_F(Fuzz,DownloadInvalid3)840 TEST_F(Fuzz, DownloadInvalid3) {
841 std::string cmd("download:-1");
842 EXPECT_EQ(fb->RawCommand("download:-1"), DEVICE_FAIL)
843 << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
844 }
845
TEST_F(Fuzz,DownloadInvalid4)846 TEST_F(Fuzz, DownloadInvalid4) {
847 std::string cmd("download:-01000000");
848 EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
849 << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
850 }
851
TEST_F(Fuzz,DownloadInvalid5)852 TEST_F(Fuzz, DownloadInvalid5) {
853 std::string cmd("download:-0100000");
854 EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
855 << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
856 }
857
TEST_F(Fuzz,DownloadInvalid6)858 TEST_F(Fuzz, DownloadInvalid6) {
859 std::string cmd("download:");
860 EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
861 << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
862 }
863
TEST_F(Fuzz,DownloadInvalid7)864 TEST_F(Fuzz, DownloadInvalid7) {
865 std::string cmd("download:01000000\0999", sizeof("download:01000000\0999"));
866 EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
867 << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
868 }
869
TEST_F(Fuzz,DownloadInvalid8)870 TEST_F(Fuzz, DownloadInvalid8) {
871 std::string cmd("download:01000000\0dkjfvijafdaiuybgidabgybr",
872 sizeof("download:01000000\0dkjfvijafdaiuybgidabgybr"));
873 EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
874 << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
875 }
876
TEST_F(Fuzz,DownloadInvalid9)877 TEST_F(Fuzz, DownloadInvalid9) {
878 std::string cmd("download:2PPPPPPPPPPPPPPPPPPPPPPPPPPPPPP");
879 EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
880 << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
881 }
882
TEST_F(Fuzz,GetVarAllSpam)883 TEST_F(Fuzz, GetVarAllSpam) {
884 auto start = std::chrono::high_resolution_clock::now();
885 std::chrono::duration<double> elapsed;
886 unsigned i = 1;
887 do {
888 std::vector<std::string> vars;
889 ASSERT_EQ(fb->GetVarAll(&vars), SUCCESS) << "Device did not respond with success after "
890 << i << "getvar:all commands in a row";
891 ASSERT_GT(vars.size(), 0)
892 << "Device did not send any INFO responses after getvar:all command";
893 elapsed = std::chrono::high_resolution_clock::now() - start;
894 } while (i++, elapsed.count() < 5);
895 }
896
TEST_F(Fuzz,BadCommandTooLarge)897 TEST_F(Fuzz, BadCommandTooLarge) {
898 std::string s = RandomString(FB_COMMAND_SZ + 1, rand_legal);
899 RetCode ret = fb->RawCommand(s);
900 EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
901 << "Device did not respond with failure after sending length " << s.size()
902 << " string of random ASCII chars";
903 if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
904 std::string s1 = RandomString(10000, rand_legal);
905 ret = fb->RawCommand(s1);
906 EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
907 << "Device did not respond with failure after sending length " << s1.size()
908 << " string of random ASCII chars";
909 if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
910 std::string s2 = RandomString(10000, rand_illegal);
911 ret = fb->RawCommand(s2);
912 EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
913 << "Device did not respond with failure after sending length " << s2.size()
914 << " string of random non-ASCII chars";
915 if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
916 std::string s3 = RandomString(10000, rand_char);
917 ret = fb->RawCommand(s3);
918 EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
919 << "Device did not respond with failure after sending length " << s3.size()
920 << " string of random chars";
921 if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
922
923 std::string s4 = RandomString(10 * 1024 * 1024, rand_legal);
924 ret = fb->RawCommand(s);
925 EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
926 << "Device did not respond with failure after sending length " << s4.size()
927 << " string of random ASCII chars ";
928 if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
929
930 ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
931 std::string resp;
932 EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "Device is unresponsive to getvar command";
933 }
934
TEST_F(Fuzz,CommandTooLarge)935 TEST_F(Fuzz, CommandTooLarge) {
936 for (const std::string& s : CMDS) {
937 std::string rs = RandomString(10000, rand_char);
938 RetCode ret;
939 ret = fb->RawCommand(s + rs);
940 EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
941 << "Device did not respond with failure " << ret << "after '" << s + rs << "'";
942 if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
943 ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
944 std::string resp;
945 EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
946 << "Device is unresponsive to getvar command";
947 }
948 }
949
TEST_F(Fuzz,CommandMissingArgs)950 TEST_F(Fuzz, CommandMissingArgs) {
951 for (const std::string& s : CMDS) {
952 if (s.back() == ':') {
953 EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)
954 << "Device did not respond with failure after '" << s << "'";
955 std::string sub(s.begin(), s.end() - 1);
956 EXPECT_EQ(fb->RawCommand(sub), DEVICE_FAIL)
957 << "Device did not respond with failure after '" << sub << "'";
958 } else {
959 std::string rs = RandomString(10, rand_illegal);
960 EXPECT_EQ(fb->RawCommand(rs + s), DEVICE_FAIL)
961 << "Device did not respond with failure after '" << rs + s << "'";
962 }
963 std::string resp;
964 EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
965 << "Device is unresponsive to getvar command";
966 }
967 }
968
TEST_F(Fuzz,SparseZeroLength)969 TEST_F(Fuzz, SparseZeroLength) {
970 SparseWrapper sparse(4096, 0);
971 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
972 RetCode ret = fb->Download(*sparse);
973 // Two ways to handle it
974 if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
975 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
976 << "Flashing zero length sparse image did not fail: " << sparse.Rep();
977 }
978 ret = fb->Download(*sparse, true);
979 if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
980 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
981 << "Flashing zero length sparse image did not fail " << sparse.Rep();
982 }
983 }
984
TEST_F(Fuzz,SparseZeroBlkSize)985 TEST_F(Fuzz, SparseZeroBlkSize) {
986 // handcrafted malform sparse file with zero as block size
987 const std::vector<char> buf = {
988 '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
989 '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
990 '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01',
991 '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
992
993 ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
994 ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
995
996 // It can either reject this download or reject it during flash
997 if (HandleResponse() != DEVICE_FAIL) {
998 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
999 << "Flashing a zero block size in sparse file should fail";
1000 }
1001 }
1002
TEST_F(Fuzz,SparseVeryLargeBlkSize)1003 TEST_F(Fuzz, SparseVeryLargeBlkSize) {
1004 // handcrafted sparse file with block size of ~4GB and divisible 4
1005 const std::vector<char> buf = {
1006 '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
1007 '\x00', '\xF0', '\xFF', '\xFF', '\xFF', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
1008 '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00', '\x01',
1009 '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
1010
1011 ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
1012 ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
1013 ASSERT_EQ(HandleResponse(), SUCCESS) << "Not receive okay";
1014 ASSERT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed";
1015 }
1016
TEST_F(Fuzz,SparseTrimmed)1017 TEST_F(Fuzz, SparseTrimmed) {
1018 // handcrafted malform sparse file which is trimmed
1019 const std::vector<char> buf = {
1020 '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
1021 '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
1022 '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
1023 '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'};
1024
1025 ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
1026 ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
1027
1028 // It can either reject this download or reject it during flash
1029 if (HandleResponse() != DEVICE_FAIL) {
1030 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
1031 << "Flashing a trimmed sparse file should fail";
1032 }
1033 }
1034
TEST_F(Fuzz,SparseInvalidChurk)1035 TEST_F(Fuzz, SparseInvalidChurk) {
1036 // handcrafted malform sparse file with invalid churk
1037 const std::vector<char> buf = {
1038 '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
1039 '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
1040 '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
1041 '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
1042
1043 ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
1044 ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
1045
1046 // It can either reject this download or reject it during flash
1047 if (HandleResponse() != DEVICE_FAIL) {
1048 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
1049 << "Flashing a sparse file with invalid churk should fail";
1050 }
1051 }
1052
TEST_F(Fuzz,SparseTooManyChunks)1053 TEST_F(Fuzz, SparseTooManyChunks) {
1054 SparseWrapper sparse(4096, 4096); // 1 block, but we send two chunks that will use 2 blocks
1055 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
1056 std::vector<char> buf = RandomBuf(4096);
1057 ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
1058 << "Adding data failed to sparse file: " << sparse.Rep();
1059 // We take advantage of the fact the sparse library does not check this
1060 ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, 1), 0)
1061 << "Adding fill to sparse file failed: " << sparse.Rep();
1062
1063 RetCode ret = fb->Download(*sparse);
1064 // Two ways to handle it
1065 if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
1066 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
1067 << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
1068 << sparse.Rep();
1069 }
1070 ret = fb->Download(*sparse, true);
1071 if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
1072 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
1073 << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
1074 << sparse.Rep();
1075 }
1076 }
1077
TEST_F(Fuzz,USBResetSpam)1078 TEST_F(Fuzz, USBResetSpam) {
1079 auto start = std::chrono::high_resolution_clock::now();
1080 std::chrono::duration<double> elapsed;
1081 int i = 0;
1082 do {
1083 ASSERT_EQ(transport->Reset(), 0) << "USB Reset failed after " << i << " resets in a row";
1084 elapsed = std::chrono::high_resolution_clock::now() - start;
1085 } while (i++, elapsed.count() < 5);
1086 std::string resp;
1087 EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
1088 << "getvar failed after " << i << " USB reset(s) in a row";
1089 }
1090
TEST_F(Fuzz,USBResetCommandSpam)1091 TEST_F(Fuzz, USBResetCommandSpam) {
1092 auto start = std::chrono::high_resolution_clock::now();
1093 std::chrono::duration<double> elapsed;
1094 do {
1095 std::string resp;
1096 std::vector<std::string> all;
1097 ASSERT_EQ(transport->Reset(), 0) << "USB Reset failed";
1098 EXPECT_EQ(fb->GetVarAll(&all), SUCCESS) << "getvar:all failed after USB reset";
1099 EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "getvar:product failed";
1100 elapsed = std::chrono::high_resolution_clock::now() - start;
1101 } while (elapsed.count() < 10);
1102 }
1103
TEST_F(Fuzz,USBResetAfterDownload)1104 TEST_F(Fuzz, USBResetAfterDownload) {
1105 std::vector<char> buf;
1106 buf.resize(1000000);
1107 EXPECT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Download command failed";
1108 EXPECT_EQ(transport->Reset(), 0) << "USB Reset failed";
1109 std::vector<std::string> all;
1110 EXPECT_EQ(fb->GetVarAll(&all), SUCCESS) << "getvar:all failed after USB reset.";
1111 }
1112
1113 // Getvar XML tests
TEST_P(ExtensionsGetVarConformance,VarExists)1114 TEST_P(ExtensionsGetVarConformance, VarExists) {
1115 std::string resp;
1116 EXPECT_EQ(fb->GetVar(GetParam().first, &resp), SUCCESS);
1117 }
1118
TEST_P(ExtensionsGetVarConformance,VarMatchesRegex)1119 TEST_P(ExtensionsGetVarConformance, VarMatchesRegex) {
1120 std::string resp;
1121 ASSERT_EQ(fb->GetVar(GetParam().first, &resp), SUCCESS);
1122 std::smatch sm;
1123 std::regex_match(resp, sm, GetParam().second.regex);
1124 EXPECT_FALSE(sm.empty()) << "The regex did not match";
1125 }
1126
1127 INSTANTIATE_TEST_CASE_P(XMLGetVar, ExtensionsGetVarConformance,
1128 ::testing::ValuesIn(GETVAR_XML_TESTS));
1129
TEST_P(AnyPartition,ReportedGetVarAll)1130 TEST_P(AnyPartition, ReportedGetVarAll) {
1131 // As long as the partition is reported in INFO, it would be tested by generic Conformance
1132 std::vector<std::tuple<std::string, uint64_t>> parts;
1133 ASSERT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
1134 const std::string name = GetParam().first;
1135 if (GetParam().second.slots) {
1136 auto matcher = [&](const std::tuple<std::string, uint32_t>& tup) {
1137 return std::get<0>(tup) == name + "_a";
1138 };
1139 EXPECT_NE(std::find_if(parts.begin(), parts.end(), matcher), parts.end())
1140 << "partition '" + name + "_a' not reported in getvar:all";
1141 } else {
1142 auto matcher = [&](const std::tuple<std::string, uint32_t>& tup) {
1143 return std::get<0>(tup) == name;
1144 };
1145 EXPECT_NE(std::find_if(parts.begin(), parts.end(), matcher), parts.end())
1146 << "partition '" + name + "' not reported in getvar:all";
1147 }
1148 }
1149
TEST_P(AnyPartition,Hashable)1150 TEST_P(AnyPartition, Hashable) {
1151 const std::string name = GetParam().first;
1152 if (!config.checksum.empty()) { // We can use hash to validate
1153 for (const auto& part_name : real_parts) {
1154 // Get hash
1155 std::string hash;
1156 int retcode;
1157 std::string err_msg;
1158 if (GetParam().second.hashable) {
1159 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
1160 << err_msg;
1161 EXPECT_EQ(retcode, 0) << err_msg;
1162 } else { // Make sure it fails
1163 const std::string cmd = config.checksum + ' ' + part_name;
1164 EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
1165 << part_name + " is marked as non-hashable, but hashing did not fail";
1166 }
1167 }
1168 }
1169 }
1170
TEST_P(WriteablePartition,FlashCheck)1171 TEST_P(WriteablePartition, FlashCheck) {
1172 const std::string name = GetParam().first;
1173 auto part_info = GetParam().second;
1174
1175 for (const auto& part_name : real_parts) {
1176 std::vector<char> buf = RandomBuf(max_flash, rand_char);
1177 EXPECT_EQ(fb->FlashPartition(part_name, buf), part_info.parsed ? DEVICE_FAIL : SUCCESS)
1178 << "A partition with an image parsed by the bootloader should reject random "
1179 "garbage "
1180 "otherwise it should succeed";
1181 }
1182 }
1183
TEST_P(WriteablePartition,EraseCheck)1184 TEST_P(WriteablePartition, EraseCheck) {
1185 const std::string name = GetParam().first;
1186
1187 for (const auto& part_name : real_parts) {
1188 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1189 }
1190 }
1191
TEST_P(WriteHashNonParsedPartition,EraseZerosData)1192 TEST_P(WriteHashNonParsedPartition, EraseZerosData) {
1193 const std::string name = GetParam().first;
1194
1195 for (const auto& part_name : real_parts) {
1196 std::string err_msg;
1197 int retcode;
1198 const std::vector<char> buf = RandomBuf(max_flash, rand_char);
1199 // Partition is too big to write to entire thing
1200 // This can eventually be supported by using sparse images if too large
1201 if (max_flash < part_size) {
1202 std::string hash_before, hash_after;
1203 ASSERT_EQ(fb->FlashPartition(part_name, buf), SUCCESS);
1204 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1205 << err_msg;
1206 ASSERT_EQ(retcode, 0) << err_msg;
1207 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1208 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1209 << err_msg;
1210 ASSERT_EQ(retcode, 0) << err_msg;
1211 EXPECT_NE(hash_before, hash_after)
1212 << "The partition hash for " + part_name +
1213 " did not change after erasing a known value";
1214 } else {
1215 std::string hash_zeros, hash_ones, hash_middle, hash_after;
1216 const std::vector<char> buf_zeros(max_flash, 0);
1217 const std::vector<char> buf_ones(max_flash, -1); // All bits are set to 1
1218 ASSERT_EQ(fb->FlashPartition(part_name, buf_zeros), SUCCESS);
1219 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_zeros, &retcode, &err_msg))
1220 << err_msg;
1221 ASSERT_EQ(retcode, 0) << err_msg;
1222 ASSERT_EQ(fb->FlashPartition(part_name, buf_ones), SUCCESS);
1223 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_ones, &retcode, &err_msg))
1224 << err_msg;
1225 ASSERT_EQ(retcode, 0) << err_msg;
1226 ASSERT_NE(hash_zeros, hash_ones)
1227 << "Hashes of partion should not be the same when all bytes are 0xFF or 0x00";
1228 ASSERT_EQ(fb->FlashPartition(part_name, buf), SUCCESS);
1229 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_middle, &retcode, &err_msg))
1230 << err_msg;
1231 ASSERT_EQ(retcode, 0) << err_msg;
1232 ASSERT_NE(hash_zeros, hash_middle)
1233 << "Hashes of partion are the same when all bytes are 0x00 or test payload";
1234 ASSERT_NE(hash_ones, hash_middle)
1235 << "Hashes of partion are the same when all bytes are 0xFF or test payload";
1236 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1237 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1238 << err_msg;
1239 ASSERT_EQ(retcode, 0) << err_msg;
1240 EXPECT_TRUE(hash_zeros == hash_after || hash_ones == hash_after)
1241 << "Erasing " + part_name + " should set all the bytes to 0xFF or 0x00";
1242 }
1243 }
1244 }
1245
1246 // Only partitions that we can write and hash (name, fixture), TEST_P is (Fixture, test_name)
1247 INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteHashNonParsed, WriteHashNonParsedPartition,
1248 ::testing::ValuesIn(PARTITION_XML_WRITE_HASH_NONPARSED));
1249
1250 INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteHashable, WriteHashablePartition,
1251 ::testing::ValuesIn(PARTITION_XML_WRITE_HASHABLE));
1252
1253 // only partitions writeable
1254 INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteable, WriteablePartition,
1255 ::testing::ValuesIn(PARTITION_XML_WRITEABLE));
1256
1257 // Every partition
1258 INSTANTIATE_TEST_CASE_P(XMLPartitionsAll, AnyPartition, ::testing::ValuesIn(PARTITION_XML_TESTS));
1259
1260 // Partition Fuzz tests
TEST_P(FuzzWriteablePartition,BoundsCheck)1261 TEST_P(FuzzWriteablePartition, BoundsCheck) {
1262 const std::string name = GetParam().first;
1263 auto part_info = GetParam().second;
1264
1265 for (const auto& part_name : real_parts) {
1266 // try and flash +1 too large, first erase and get a hash, make sure it does not change
1267 std::vector<char> buf = RandomBuf(max_flash + 1); // One too large
1268 if (part_info.hashable) {
1269 std::string hash_before, hash_after, err_msg;
1270 int retcode;
1271 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1272 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1273 << err_msg;
1274 ASSERT_EQ(retcode, 0) << err_msg;
1275 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1276 << "Flashing an image 1 byte too large to " + part_name + " did not fail";
1277 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1278 << err_msg;
1279 ASSERT_EQ(retcode, 0) << err_msg;
1280 EXPECT_EQ(hash_before, hash_after)
1281 << "Flashing too large of an image resulted in a changed partition hash for " +
1282 part_name;
1283 } else {
1284 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1285 << "Flashing an image 1 byte too large to " + part_name + " did not fail";
1286 }
1287 }
1288 }
1289
1290 INSTANTIATE_TEST_CASE_P(XMLFuzzPartitionsWriteable, FuzzWriteablePartition,
1291 ::testing::ValuesIn(PARTITION_XML_WRITEABLE));
1292
1293 // A parsed partition should have magic and such that is checked by the bootloader
1294 // Attempting to flash a random single byte should definately fail
TEST_P(FuzzWriteableParsedPartition,FlashGarbageImageSmall)1295 TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageSmall) {
1296 const std::string name = GetParam().first;
1297 auto part_info = GetParam().second;
1298
1299 for (const auto& part_name : real_parts) {
1300 std::vector<char> buf = RandomBuf(1);
1301 if (part_info.hashable) {
1302 std::string hash_before, hash_after, err_msg;
1303 int retcode;
1304 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1305 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1306 << err_msg;
1307 ASSERT_EQ(retcode, 0) << err_msg;
1308 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1309 << "A parsed partition should fail on a single byte";
1310 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1311 << err_msg;
1312 ASSERT_EQ(retcode, 0) << err_msg;
1313 EXPECT_EQ(hash_before, hash_after)
1314 << "Flashing a single byte to parsed partition " + part_name +
1315 " should fail and not change the partition hash";
1316 } else {
1317 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1318 << "Flashing a 1 byte image to a parsed partition should fail";
1319 }
1320 }
1321 }
1322
TEST_P(FuzzWriteableParsedPartition,FlashGarbageImageLarge)1323 TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge) {
1324 const std::string name = GetParam().first;
1325 auto part_info = GetParam().second;
1326
1327 for (const auto& part_name : real_parts) {
1328 std::vector<char> buf = RandomBuf(max_flash);
1329 if (part_info.hashable) {
1330 std::string hash_before, hash_after, err_msg;
1331 int retcode;
1332 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1333 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1334 << err_msg;
1335 ASSERT_EQ(retcode, 0) << err_msg;
1336 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1337 << "A parsed partition should not accept randomly generated images";
1338 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1339 << err_msg;
1340 ASSERT_EQ(retcode, 0) << err_msg;
1341 EXPECT_EQ(hash_before, hash_after)
1342 << "The hash of the partition has changed after attempting to flash garbage to "
1343 "a parsed partition";
1344 } else {
1345 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1346 << "A parsed partition should not accept randomly generated images";
1347 }
1348 }
1349 }
1350
TEST_P(FuzzWriteableParsedPartition,FlashGarbageImageLarge2)1351 TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge2) {
1352 const std::string name = GetParam().first;
1353 auto part_info = GetParam().second;
1354
1355 for (const auto& part_name : real_parts) {
1356 std::vector<char> buf(max_flash, -1); // All 1's
1357 if (part_info.hashable) {
1358 std::string hash_before, hash_after, err_msg;
1359 int retcode;
1360 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1361 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1362 << err_msg;
1363 ASSERT_EQ(retcode, 0) << err_msg;
1364 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1365 << "A parsed partition should not accept a image of all 0xFF";
1366 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1367 << err_msg;
1368 ASSERT_EQ(retcode, 0) << err_msg;
1369 EXPECT_EQ(hash_before, hash_after)
1370 << "The hash of the partition has changed after attempting to flash garbage to "
1371 "a parsed partition";
1372 } else {
1373 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1374 << "A parsed partition should not accept a image of all 0xFF";
1375 }
1376 }
1377 }
1378
TEST_P(FuzzWriteableParsedPartition,FlashGarbageImageLarge3)1379 TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge3) {
1380 const std::string name = GetParam().first;
1381 auto part_info = GetParam().second;
1382
1383 for (const auto& part_name : real_parts) {
1384 std::vector<char> buf(max_flash, 0); // All 0's
1385 if (part_info.hashable) {
1386 std::string hash_before, hash_after, err_msg;
1387 int retcode;
1388 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1389 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1390 << err_msg;
1391 ASSERT_EQ(retcode, 0) << err_msg;
1392 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1393 << "A parsed partition should not accept a image of all 0x00";
1394 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1395 << err_msg;
1396 ASSERT_EQ(retcode, 0) << err_msg;
1397 EXPECT_EQ(hash_before, hash_after)
1398 << "The hash of the partition has changed after attempting to flash garbage to "
1399 "a parsed partition";
1400 } else {
1401 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1402 << "A parsed partition should not accept a image of all 0x00";
1403 }
1404 }
1405 }
1406
1407 INSTANTIATE_TEST_CASE_P(XMLFuzzPartitionsWriteableParsed, FuzzWriteableParsedPartition,
1408 ::testing::ValuesIn(PARTITION_XML_WRITE_PARSED));
1409
1410 // Make sure all attempts to flash things are rejected
TEST_P(FuzzAnyPartitionLocked,RejectFlash)1411 TEST_P(FuzzAnyPartitionLocked, RejectFlash) {
1412 std::vector<char> buf = RandomBuf(5);
1413 for (const auto& part_name : real_parts) {
1414 ASSERT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1415 << "Flashing a partition should always fail in locked mode";
1416 }
1417 }
1418
1419 INSTANTIATE_TEST_CASE_P(XMLFuzzAnyPartitionLocked, FuzzAnyPartitionLocked,
1420 ::testing::ValuesIn(PARTITION_XML_TESTS));
1421
1422 // Test flashing unlock erases userdata
TEST_P(UserdataPartition,UnlockErases)1423 TEST_P(UserdataPartition, UnlockErases) {
1424 // Get hash after an erase
1425 int retcode;
1426 std::string err_msg, hash_before, hash_buf, hash_after;
1427 ASSERT_EQ(fb->Erase("userdata"), SUCCESS) << "Erasing uesrdata failed";
1428 ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_before, &retcode, &err_msg)) << err_msg;
1429 ASSERT_EQ(retcode, 0) << err_msg;
1430
1431 // Write garbage
1432 std::vector<char> buf = RandomBuf(max_flash / 2);
1433 ASSERT_EQ(fb->FlashPartition("userdata", buf), SUCCESS);
1434 ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_buf, &retcode, &err_msg)) << err_msg;
1435 ASSERT_EQ(retcode, 0) << err_msg;
1436
1437 // Validity check of hash
1438 EXPECT_NE(hash_before, hash_buf)
1439 << "Writing a random buffer to 'userdata' had the same hash as after erasing it";
1440 SetLockState(true); // Lock the device
1441
1442 SetLockState(false); // Unlock the device (should cause erase)
1443 ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_after, &retcode, &err_msg)) << err_msg;
1444 ASSERT_EQ(retcode, 0) << err_msg;
1445
1446 EXPECT_NE(hash_after, hash_buf) << "Unlocking the device did not cause the hash of userdata to "
1447 "change (i.e. it was not erased as required)";
1448 EXPECT_EQ(hash_after, hash_before) << "Unlocking the device did not produce the same hash of "
1449 "userdata as after doing an erase to userdata";
1450 }
1451
1452 // This is a hack to make this test disapeer if there is not a checsum, userdata is not hashable,
1453 // or userdata is not marked to be writeable in testing
1454 INSTANTIATE_TEST_CASE_P(XMLUserdataLocked, UserdataPartition,
1455 ::testing::ValuesIn(PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE));
1456
1457 // Packed images test
TEST_P(ExtensionsPackedValid,TestDeviceUnpack)1458 TEST_P(ExtensionsPackedValid, TestDeviceUnpack) {
1459 const std::string& packed_name = GetParam().first;
1460 const std::string& packed_image = GetParam().second.packed_img;
1461 const std::string& unpacked = GetParam().second.unpacked_dir;
1462
1463 // First we need to check for existence of images
1464 const extension::Configuration::PackedInfo& info = config.packed[packed_name];
1465
1466 const auto flash_part = [&](const std::string fname, const std::string part_name) {
1467 FILE* to_flash = fopen((SEARCH_PATH + fname).c_str(), "rb");
1468 ASSERT_NE(to_flash, nullptr) << "'" << fname << "'"
1469 << " failed to open for flashing";
1470 int fd = fileno(to_flash);
1471 size_t fsize = lseek(fd, 0, SEEK_END);
1472 ASSERT_GT(fsize, 0) << fname + " appears to be an empty image";
1473 ASSERT_EQ(fb->FlashPartition(part_name, fd, fsize), SUCCESS);
1474 fclose(to_flash);
1475 };
1476
1477 // We first need to set the slot count
1478 std::string var;
1479 int num_slots = 1;
1480 if (info.slots) {
1481 ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
1482 num_slots = strtol(var.c_str(), nullptr, 10);
1483 } else {
1484 for (const auto& part : info.children) {
1485 EXPECT_FALSE(config.partitions[part].slots)
1486 << "A partition can not have slots if the packed image does not";
1487 }
1488 }
1489
1490 for (int i = 0; i < num_slots; i++) {
1491 std::unordered_map<std::string, std::string> initial_hashes;
1492 const std::string packed_suffix =
1493 info.slots ? android::base::StringPrintf("_%c", 'a' + i) : "";
1494
1495 // Flash the paritions manually and get hash
1496 for (const auto& part : info.children) {
1497 const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
1498 const std::string suffix = part_info.slots ? packed_suffix : "";
1499 const std::string part_name = part + suffix;
1500
1501 ASSERT_EQ(fb->Erase(part_name), SUCCESS);
1502 const std::string fpath = unpacked + '/' + part + ".img";
1503 ASSERT_NO_FATAL_FAILURE(flash_part(fpath, part_name))
1504 << "Failed to flash '" + fpath + "'";
1505 // If the partition is hashable we store it
1506 if (part_info.hashable) {
1507 std::string hash, err_msg;
1508 int retcode;
1509 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
1510 << err_msg;
1511 ASSERT_EQ(retcode, 0) << err_msg;
1512 initial_hashes[part] = hash;
1513 }
1514 }
1515
1516 // erase once at the end, to avoid false positives if flashing does nothing
1517 for (const auto& part : info.children) {
1518 const std::string suffix = config.partitions[part].slots ? packed_suffix : "";
1519 ASSERT_EQ(fb->Erase(part + suffix), SUCCESS);
1520 }
1521
1522 // Now we flash the packed image and compare our hashes
1523 ASSERT_NO_FATAL_FAILURE(flash_part(packed_image, packed_name + packed_suffix));
1524
1525 for (const auto& part : info.children) {
1526 const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
1527 // If the partition is hashable we check it
1528 if (part_info.hashable) {
1529 const std::string suffix = part_info.slots ? packed_suffix : "";
1530 const std::string part_name = part + suffix;
1531 std::string hash, err_msg;
1532 int retcode;
1533 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
1534 << err_msg;
1535 ASSERT_EQ(retcode, 0) << err_msg;
1536 std::string msg =
1537 "The hashes between flashing the packed image and directly flashing '" +
1538 part_name + "' does not match";
1539 EXPECT_EQ(hash, initial_hashes[part]) << msg;
1540 }
1541 }
1542 }
1543 }
1544
1545 INSTANTIATE_TEST_CASE_P(XMLTestPacked, ExtensionsPackedValid,
1546 ::testing::ValuesIn(PACKED_XML_SUCCESS_TESTS));
1547
1548 // Packed images test
TEST_P(ExtensionsPackedInvalid,TestDeviceUnpack)1549 TEST_P(ExtensionsPackedInvalid, TestDeviceUnpack) {
1550 const std::string& packed_name = GetParam().first;
1551 const std::string& packed_image = GetParam().second.packed_img;
1552
1553 // First we need to check for existence of images
1554 const extension::Configuration::PackedInfo& info = config.packed[packed_name];
1555
1556 // We first need to set the slot count
1557 std::string var;
1558 int num_slots = 1;
1559 if (info.slots) {
1560 ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
1561 num_slots = strtol(var.c_str(), nullptr, 10);
1562 } else {
1563 for (const auto& part : info.children) {
1564 EXPECT_FALSE(config.partitions[part].slots)
1565 << "A partition can not have slots if the packed image does not";
1566 }
1567 }
1568
1569 for (int i = 0; i < num_slots; i++) {
1570 std::unordered_map<std::string, std::string> initial_hashes;
1571 const std::string packed_suffix =
1572 info.slots ? android::base::StringPrintf("_%c", 'a' + i) : "";
1573
1574 // manually and get hash
1575 for (const auto& part : info.children) {
1576 const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
1577 const std::string suffix = part_info.slots ? packed_suffix : "";
1578 const std::string part_name = part + suffix;
1579
1580 // If the partition is hashable we store it
1581 if (part_info.hashable) {
1582 std::string hash, err_msg;
1583 int retcode;
1584 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
1585 << err_msg;
1586 ASSERT_EQ(retcode, 0) << err_msg;
1587 initial_hashes[part] = hash;
1588 }
1589 }
1590
1591 // Attempt to flash the invalid file
1592 FILE* to_flash = fopen((SEARCH_PATH + packed_image).c_str(), "rb");
1593 ASSERT_NE(to_flash, nullptr) << "'" << packed_image << "'"
1594 << " failed to open for flashing";
1595 int fd = fileno(to_flash);
1596 size_t fsize = lseek(fd, 0, SEEK_END);
1597 ASSERT_GT(fsize, 0) << packed_image + " appears to be an empty image";
1598 ASSERT_EQ(fb->FlashPartition(packed_name + packed_suffix, fd, fsize), DEVICE_FAIL)
1599 << "Expected flashing to fail for " + packed_image;
1600 fclose(to_flash);
1601
1602 for (const auto& part : info.children) {
1603 const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
1604 // If the partition is hashable we check it
1605 if (part_info.hashable) {
1606 const std::string suffix = part_info.slots ? packed_suffix : "";
1607 const std::string part_name = part + suffix;
1608 std::string hash, err_msg;
1609 int retcode;
1610 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
1611 << err_msg;
1612 ASSERT_EQ(retcode, 0) << err_msg;
1613 std::string msg = "Flashing an invalid image changed the hash of '" + part_name;
1614 EXPECT_EQ(hash, initial_hashes[part]) << msg;
1615 }
1616 }
1617 }
1618 }
1619
1620 INSTANTIATE_TEST_CASE_P(XMLTestPacked, ExtensionsPackedInvalid,
1621 ::testing::ValuesIn(PACKED_XML_FAIL_TESTS));
1622
1623 // OEM xml tests
TEST_P(ExtensionsOemConformance,RunOEMTest)1624 TEST_P(ExtensionsOemConformance, RunOEMTest) {
1625 const std::string& cmd = std::get<0>(GetParam());
1626 // bool restricted = std::get<1>(GetParam());
1627 const extension::Configuration::CommandTest& test = std::get<2>(GetParam());
1628
1629 const RetCode expect = (test.expect == extension::FAIL) ? DEVICE_FAIL : SUCCESS;
1630
1631 // Does the test require staging something?
1632 if (!test.input.empty()) { // Non-empty string
1633 FILE* to_stage = fopen((SEARCH_PATH + test.input).c_str(), "rb");
1634 ASSERT_NE(to_stage, nullptr) << "'" << test.input << "'"
1635 << " failed to open for staging";
1636 int fd = fileno(to_stage);
1637 size_t fsize = lseek(fd, 0, SEEK_END);
1638 std::string var;
1639 EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS);
1640 int64_t size = strtoll(var.c_str(), nullptr, 16);
1641 EXPECT_LT(fsize, size) << "'" << test.input << "'"
1642 << " is too large for staging";
1643 ASSERT_EQ(fb->Download(fd, fsize), SUCCESS) << "'" << test.input << "'"
1644 << " failed to download for staging";
1645 fclose(to_stage);
1646 }
1647 // Run the command
1648 int dsize = -1;
1649 std::string resp;
1650 const std::string full_cmd = "oem " + cmd + " " + test.arg;
1651 ASSERT_EQ(fb->RawCommand(full_cmd, &resp, nullptr, &dsize), expect);
1652
1653 // This is how we test if indeed data response
1654 if (test.expect == extension::DATA) {
1655 EXPECT_GT(dsize, 0);
1656 }
1657
1658 // Validate response if neccesary
1659 if (!test.regex_str.empty()) {
1660 std::smatch sm;
1661 std::regex_match(resp, sm, test.regex);
1662 EXPECT_FALSE(sm.empty()) << "The oem regex did not match";
1663 }
1664
1665 // If payload, we validate that as well
1666 const std::vector<std::string> args = SplitBySpace(test.validator);
1667 if (args.size()) {
1668 // Save output
1669 const std::string save_loc =
1670 OUTPUT_PATH + (test.output.empty() ? DEFAULT_OUPUT_NAME : test.output);
1671 std::string resp;
1672 ASSERT_EQ(fb->Upload(save_loc, &resp), SUCCESS)
1673 << "Saving output file failed with (" << fb->Error() << ") " << resp;
1674 // Build the arguments to the validator
1675 std::vector<std::string> prog_args(args.begin() + 1, args.end());
1676 prog_args.push_back(full_cmd); // Pass in the full command
1677 prog_args.push_back(save_loc); // Pass in the save location
1678 // Run the validation program
1679 int pipe;
1680 const pid_t pid = StartProgram(args[0], prog_args, &pipe);
1681 ASSERT_GT(pid, 0) << "Failed to launch validation program: " << args[0];
1682 std::string error_msg;
1683 int ret = WaitProgram(pid, pipe, &error_msg);
1684 EXPECT_EQ(ret, 0) << error_msg; // Program exited correctly
1685 }
1686 }
1687
1688 INSTANTIATE_TEST_CASE_P(XMLOEM, ExtensionsOemConformance, ::testing::ValuesIn(OEM_XML_TESTS));
1689
1690 // Sparse Tests
TEST_P(SparseTestPartition,SparseSingleBlock)1691 TEST_P(SparseTestPartition, SparseSingleBlock) {
1692 const std::string name = GetParam().first;
1693 auto part_info = GetParam().second;
1694 const std::string part_name = name + (part_info.slots ? "_a" : "");
1695 SparseWrapper sparse(4096, 4096);
1696 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
1697 std::vector<char> buf = RandomBuf(4096);
1698 ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
1699 << "Adding data failed to sparse file: " << sparse.Rep();
1700
1701 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
1702 EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
1703 std::string hash, hash_new, err_msg;
1704 int retcode;
1705 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
1706 ASSERT_EQ(retcode, 0) << err_msg;
1707 // Now flash it the non-sparse way
1708 EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
1709 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
1710 ASSERT_EQ(retcode, 0) << err_msg;
1711
1712 EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
1713 "methods did not result in the same hash";
1714 }
1715
TEST_P(SparseTestPartition,SparseFill)1716 TEST_P(SparseTestPartition, SparseFill) {
1717 const std::string name = GetParam().first;
1718 auto part_info = GetParam().second;
1719 const std::string part_name = name + (part_info.slots ? "_a" : "");
1720 int64_t size = (max_dl / 4096) * 4096;
1721 SparseWrapper sparse(4096, size);
1722 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
1723 ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size, 0), 0)
1724 << "Adding data failed to sparse file: " << sparse.Rep();
1725
1726 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
1727 EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
1728 std::string hash, hash_new, err_msg;
1729 int retcode;
1730 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
1731 ASSERT_EQ(retcode, 0) << err_msg;
1732 // Now flash it the non-sparse way
1733 std::vector<char> buf(size);
1734 for (auto iter = buf.begin(); iter < buf.end(); iter += 4) {
1735 iter[0] = 0xef;
1736 iter[1] = 0xbe;
1737 iter[2] = 0xad;
1738 iter[3] = 0xde;
1739 }
1740 EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
1741 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
1742 ASSERT_EQ(retcode, 0) << err_msg;
1743
1744 EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
1745 "methods did not result in the same hash";
1746 }
1747
1748 // This tests to make sure it does not overwrite previous flashes
TEST_P(SparseTestPartition,SparseMultiple)1749 TEST_P(SparseTestPartition, SparseMultiple) {
1750 const std::string name = GetParam().first;
1751 auto part_info = GetParam().second;
1752 const std::string part_name = name + (part_info.slots ? "_a" : "");
1753 int64_t size = (max_dl / 4096) * 4096;
1754 SparseWrapper sparse(4096, size / 2);
1755 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
1756 ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size / 2, 0), 0)
1757 << "Adding data failed to sparse file: " << sparse.Rep();
1758 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
1759 EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
1760
1761 SparseWrapper sparse2(4096, size / 2);
1762 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
1763 std::vector<char> buf = RandomBuf(size / 2);
1764 ASSERT_EQ(sparse_file_add_data(*sparse2, buf.data(), buf.size(), (size / 2) / 4096), 0)
1765 << "Adding data failed to sparse file: " << sparse2.Rep();
1766 EXPECT_EQ(fb->Download(*sparse2), SUCCESS) << "Download sparse failed: " << sparse2.Rep();
1767 EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse2.Rep();
1768
1769 std::string hash, hash_new, err_msg;
1770 int retcode;
1771 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
1772 ASSERT_EQ(retcode, 0) << err_msg;
1773 // Now flash it the non-sparse way
1774 std::vector<char> fbuf(size);
1775 for (auto iter = fbuf.begin(); iter < fbuf.begin() + size / 2; iter += 4) {
1776 iter[0] = 0xef;
1777 iter[1] = 0xbe;
1778 iter[2] = 0xad;
1779 iter[3] = 0xde;
1780 }
1781 fbuf.assign(buf.begin(), buf.end());
1782 EXPECT_EQ(fb->FlashPartition(part_name, fbuf), SUCCESS) << "Flashing image failed: ";
1783 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
1784 ASSERT_EQ(retcode, 0) << err_msg;
1785
1786 EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
1787 "methods did not result in the same hash";
1788 }
1789
1790 INSTANTIATE_TEST_CASE_P(XMLSparseTest, SparseTestPartition,
1791 ::testing::ValuesIn(SINGLE_PARTITION_XML_WRITE_HASHABLE));
1792
GenerateXmlTests(const extension::Configuration & config)1793 void GenerateXmlTests(const extension::Configuration& config) {
1794 // Build the getvar tests
1795 for (const auto& it : config.getvars) {
1796 GETVAR_XML_TESTS.push_back(std::make_pair(it.first, it.second));
1797 }
1798
1799 // Build the partition tests, to interface with gtest we need to do it this way
1800 for (const auto& it : config.partitions) {
1801 const auto tup = std::make_tuple(it.first, it.second);
1802 PARTITION_XML_TESTS.push_back(tup); // All partitions
1803
1804 if (it.second.test == it.second.YES) {
1805 PARTITION_XML_WRITEABLE.push_back(tup); // All writeable partitions
1806
1807 if (it.second.hashable) {
1808 PARTITION_XML_WRITE_HASHABLE.push_back(tup); // All write and hashable
1809 if (!it.second.parsed) {
1810 PARTITION_XML_WRITE_HASH_NONPARSED.push_back(
1811 tup); // All write hashed and non-parsed
1812 }
1813 }
1814 if (it.second.parsed) {
1815 PARTITION_XML_WRITE_PARSED.push_back(tup); // All write and parsed
1816 }
1817 }
1818 }
1819
1820 // Build the packed tests, only useful if we have a hash
1821 if (!config.checksum.empty()) {
1822 for (const auto& it : config.packed) {
1823 for (const auto& test : it.second.tests) {
1824 const auto tup = std::make_tuple(it.first, test);
1825 if (test.expect == extension::OKAY) { // only testing the success case
1826 PACKED_XML_SUCCESS_TESTS.push_back(tup);
1827 } else {
1828 PACKED_XML_FAIL_TESTS.push_back(tup);
1829 }
1830 }
1831 }
1832 }
1833
1834 // This is a hack to make this test disapeer if there is not a checksum, userdata is not
1835 // hashable, or userdata is not marked to be writeable in testing
1836 const auto part_info = config.partitions.find("userdata");
1837 if (!config.checksum.empty() && part_info != config.partitions.end() &&
1838 part_info->second.hashable &&
1839 part_info->second.test == extension::Configuration::PartitionInfo::YES) {
1840 PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE.push_back(
1841 std::make_tuple(part_info->first, part_info->second));
1842 }
1843
1844 if (!PARTITION_XML_WRITE_HASHABLE.empty()) {
1845 SINGLE_PARTITION_XML_WRITE_HASHABLE.push_back(PARTITION_XML_WRITE_HASHABLE.front());
1846 }
1847
1848 // Build oem tests
1849 for (const auto& it : config.oem) {
1850 auto oem_cmd = it.second;
1851 for (const auto& t : oem_cmd.tests) {
1852 OEM_XML_TESTS.push_back(std::make_tuple(it.first, oem_cmd.restricted, t));
1853 }
1854 }
1855 }
1856
1857 } // namespace fastboot
1858
main(int argc,char ** argv)1859 int main(int argc, char** argv) {
1860 std::string err;
1861 // Parse the args
1862 const std::unordered_map<std::string, std::string> args = fastboot::ParseArgs(argc, argv, &err);
1863 if (!err.empty()) {
1864 printf("%s\n", err.c_str());
1865 return -1;
1866 }
1867
1868 if (args.find("config") != args.end()) {
1869 auto found = args.find("search_path");
1870 fastboot::SEARCH_PATH = (found != args.end()) ? found->second + "/" : "";
1871 found = args.find("output_path");
1872 fastboot::OUTPUT_PATH = (found != args.end()) ? found->second + "/" : "/tmp/";
1873 if (!fastboot::extension::ParseXml(fastboot::SEARCH_PATH + args.at("config"),
1874 &fastboot::config)) {
1875 printf("XML config parsing failed\n");
1876 return -1;
1877 }
1878 // To interface with gtest, must set global scope test variables
1879 fastboot::GenerateXmlTests(fastboot::config);
1880 }
1881
1882 if (args.find("serial") != args.end()) {
1883 fastboot::FastBootTest::device_serial = args.at("serial");
1884 }
1885
1886 setbuf(stdout, NULL); // no buffering
1887
1888 if (!fastboot::FastBootTest::IsFastbootOverTcp()) {
1889 printf("<Waiting for Device>\n");
1890 const auto matcher = [](usb_ifc_info* info) -> int {
1891 return fastboot::FastBootTest::MatchFastboot(info,
1892 fastboot::FastBootTest::device_serial);
1893 };
1894 std::unique_ptr<Transport> transport;
1895 while (!transport) {
1896 transport = usb_open(matcher);
1897 std::this_thread::sleep_for(std::chrono::milliseconds(10));
1898 }
1899 transport->Close();
1900 }
1901
1902 if (args.find("serial_port") != args.end()) {
1903 fastboot::FastBootTest::serial_port = fastboot::ConfigureSerial(args.at("serial_port"));
1904 }
1905
1906 ::testing::InitGoogleTest(&argc, argv);
1907 auto ret = RUN_ALL_TESTS();
1908 if (fastboot::FastBootTest::serial_port > 0) {
1909 close(fastboot::FastBootTest::serial_port);
1910 }
1911 return ret;
1912 }
1913