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 "fastboot_driver.h"
54 #include "usb.h"
55
56 #include "extensions.h"
57 #include "fixtures.h"
58 #include "test_utils.h"
59 #include "transport_sniffer.h"
60
61 namespace fastboot {
62
63 extension::Configuration config; // The parsed XML config
64
65 std::string SEARCH_PATH;
66 std::string OUTPUT_PATH;
67
68 // gtest's INSTANTIATE_TEST_CASE_P() must be at global scope,
69 // so our autogenerated tests must be as well
70 std::vector<std::pair<std::string, extension::Configuration::GetVar>> GETVAR_XML_TESTS;
71 std::vector<std::tuple<std::string, bool, extension::Configuration::CommandTest>> OEM_XML_TESTS;
72 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>> PARTITION_XML_TESTS;
73 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
74 PARTITION_XML_WRITEABLE;
75 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
76 PARTITION_XML_WRITE_HASHABLE;
77 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
78 PARTITION_XML_WRITE_PARSED;
79 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
80 PARTITION_XML_WRITE_HASH_NONPARSED;
81 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
82 PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE;
83 std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>>
84 PACKED_XML_SUCCESS_TESTS;
85 std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>> PACKED_XML_FAIL_TESTS;
86 // This only has 1 or zero elements so it will disappear from gtest when empty
87 std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
88 SINGLE_PARTITION_XML_WRITE_HASHABLE;
89
90 const std::string DEFAULT_OUPUT_NAME = "out.img";
91 // const char scratch_partition[] = "userdata";
92 const std::vector<std::string> CMDS{"boot", "continue", "download:", "erase:", "flash:",
93 "getvar:", "reboot", "set_active:", "upload"};
94
95 // For pretty printing we need all these overloads
operator <<(::std::ostream & os,const RetCode & ret)96 ::std::ostream& operator<<(::std::ostream& os, const RetCode& ret) {
97 return os << FastBootDriver::RCString(ret);
98 }
99
PartitionHash(FastBootDriver * fb,const std::string & part,std::string * hash,int * retcode,std::string * err_msg)100 bool PartitionHash(FastBootDriver* fb, const std::string& part, std::string* hash, int* retcode,
101 std::string* err_msg) {
102 if (config.checksum.empty()) {
103 return -1;
104 }
105
106 std::string resp;
107 std::vector<std::string> info;
108 const std::string cmd = config.checksum + ' ' + part;
109 RetCode ret;
110 if ((ret = fb->RawCommand(cmd, &resp, &info)) != SUCCESS) {
111 *err_msg =
112 android::base::StringPrintf("Hashing partition with command '%s' failed with: %s",
113 cmd.c_str(), fb->RCString(ret).c_str());
114 return false;
115 }
116 std::stringstream imploded;
117 std::copy(info.begin(), info.end(), std::ostream_iterator<std::string>(imploded, "\n"));
118
119 // If payload, we validate that as well
120 const std::vector<std::string> args = SplitBySpace(config.checksum_parser);
121 std::vector<std::string> prog_args(args.begin() + 1, args.end());
122 prog_args.push_back(resp); // Pass in the full command
123 prog_args.push_back(SEARCH_PATH + imploded.str()); // Pass in the save location
124
125 int pipe;
126 pid_t pid = StartProgram(args[0], prog_args, &pipe);
127 if (pid <= 0) {
128 *err_msg = android::base::StringPrintf("Launching hash parser '%s' failed with: %s",
129 config.checksum_parser.c_str(), strerror(errno));
130 return false;
131 }
132 *retcode = WaitProgram(pid, pipe, hash);
133 if (*retcode) {
134 // In this case the stderr pipe is a log message
135 *err_msg = android::base::StringPrintf("Hash parser '%s' failed with: %s",
136 config.checksum_parser.c_str(), hash->c_str());
137 return false;
138 }
139
140 return true;
141 }
142
SparseToBuf(sparse_file * sf,std::vector<char> * out,bool with_crc=false)143 bool SparseToBuf(sparse_file* sf, std::vector<char>* out, bool with_crc = false) {
144 int64_t len = sparse_file_len(sf, true, with_crc);
145 if (len <= 0) {
146 return false;
147 }
148 out->clear();
149 auto cb = [](void* priv, const void* data, size_t len) {
150 auto vec = static_cast<std::vector<char>*>(priv);
151 const char* cbuf = static_cast<const char*>(data);
152 vec->insert(vec->end(), cbuf, cbuf + len);
153 return 0;
154 };
155
156 return !sparse_file_callback(sf, true, with_crc, cb, out);
157 }
158
159 // Only allow alphanumeric, _, -, and .
__anon2367c8180202(char c) 160 const auto not_allowed = [](char c) -> int {
161 return !(isalnum(c) || c == '_' || c == '-' || c == '.');
162 };
163
164 // Test that USB even works
TEST(USBFunctionality,USBConnect)165 TEST(USBFunctionality, USBConnect) {
166 const auto matcher = [](usb_ifc_info* info) -> int {
167 return FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
168 };
169 Transport* transport = nullptr;
170 for (int i = 0; i < FastBootTest::MAX_USB_TRIES && !transport; i++) {
171 transport = usb_open(matcher);
172 std::this_thread::sleep_for(std::chrono::milliseconds(10));
173 }
174 ASSERT_NE(transport, nullptr) << "Could not find the fastboot device after: "
175 << 10 * FastBootTest::MAX_USB_TRIES << "ms";
176 if (transport) {
177 transport->Close();
178 delete transport;
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)
933 << "Device is unresponsive to getvar command";
934 }
935
TEST_F(Fuzz,CommandTooLarge)936 TEST_F(Fuzz, CommandTooLarge) {
937 for (const std::string& s : CMDS) {
938 std::string rs = RandomString(10000, rand_char);
939 RetCode ret;
940 ret = fb->RawCommand(s + rs);
941 EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
942 << "Device did not respond with failure " << ret << "after '" << s + rs << "'";
943 if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
944 ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
945 std::string resp;
946 EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
947 << "Device is unresponsive to getvar command";
948 }
949 }
950
TEST_F(Fuzz,CommandMissingArgs)951 TEST_F(Fuzz, CommandMissingArgs) {
952 for (const std::string& s : CMDS) {
953 if (s.back() == ':') {
954 EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)
955 << "Device did not respond with failure after '" << s << "'";
956 std::string sub(s.begin(), s.end() - 1);
957 EXPECT_EQ(fb->RawCommand(sub), DEVICE_FAIL)
958 << "Device did not respond with failure after '" << sub << "'";
959 } else {
960 std::string rs = RandomString(10, rand_illegal);
961 EXPECT_EQ(fb->RawCommand(rs + s), DEVICE_FAIL)
962 << "Device did not respond with failure after '" << rs + s << "'";
963 }
964 std::string resp;
965 EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
966 << "Device is unresponsive to getvar command";
967 }
968 }
969
TEST_F(Fuzz,SparseZeroLength)970 TEST_F(Fuzz, SparseZeroLength) {
971 SparseWrapper sparse(4096, 0);
972 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
973 RetCode ret = fb->Download(*sparse);
974 // Two ways to handle it
975 if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
976 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
977 << "Flashing zero length sparse image did not fail: " << sparse.Rep();
978 }
979 ret = fb->Download(*sparse, true);
980 if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
981 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
982 << "Flashing zero length sparse image did not fail " << sparse.Rep();
983 }
984 }
985
TEST_F(Fuzz,SparseZeroBlkSize)986 TEST_F(Fuzz, SparseZeroBlkSize) {
987 // handcrafted malform sparse file with zero as block size
988 const std::vector<char> buf = {
989 '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
990 '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
991 '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
992 '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
993 };
994
995 ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
996 ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
997
998 // It can either reject this download or reject it during flash
999 if (HandleResponse() != DEVICE_FAIL) {
1000 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
1001 << "Flashing a zero block size in sparse file should fail";
1002 }
1003 }
1004
TEST_F(Fuzz,SparseVeryLargeBlkSize)1005 TEST_F(Fuzz, SparseVeryLargeBlkSize) {
1006 // handcrafted sparse file with block size of ~4GB and divisible 4
1007 const std::vector<char> buf = {
1008 '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00',
1009 '\x1c', '\x00', '\x0c', '\x00', '\xF0', '\xFF', '\xFF', '\xFF',
1010 '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
1011 '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00',
1012 '\x01', '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00',
1013 '\x11', '\x22', '\x33', '\x44'
1014 };
1015
1016 ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
1017 ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
1018 ASSERT_EQ(HandleResponse(), SUCCESS) << "Not receive okay";
1019 ASSERT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed";
1020 }
1021
TEST_F(Fuzz,SparseTrimmed)1022 TEST_F(Fuzz, SparseTrimmed) {
1023 // handcrafted malform sparse file which is trimmed
1024 const std::vector<char> buf = {
1025 '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
1026 '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
1027 '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
1028 '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'
1029 };
1030
1031 ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
1032 ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
1033
1034 // It can either reject this download or reject it during flash
1035 if (HandleResponse() != DEVICE_FAIL) {
1036 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
1037 << "Flashing a trimmed sparse file should fail";
1038 }
1039 }
1040
TEST_F(Fuzz,SparseInvalidChurk)1041 TEST_F(Fuzz, SparseInvalidChurk) {
1042 // handcrafted malform sparse file with invalid churk
1043 const std::vector<char> buf = {
1044 '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
1045 '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
1046 '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
1047 '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
1048 };
1049
1050 ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
1051 ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
1052
1053 // It can either reject this download or reject it during flash
1054 if (HandleResponse() != DEVICE_FAIL) {
1055 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
1056 << "Flashing a sparse file with invalid churk should fail";
1057 }
1058 }
1059
TEST_F(Fuzz,SparseTooManyChunks)1060 TEST_F(Fuzz, SparseTooManyChunks) {
1061 SparseWrapper sparse(4096, 4096); // 1 block, but we send two chunks that will use 2 blocks
1062 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
1063 std::vector<char> buf = RandomBuf(4096);
1064 ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
1065 << "Adding data failed to sparse file: " << sparse.Rep();
1066 // We take advantage of the fact the sparse library does not check this
1067 ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, 1), 0)
1068 << "Adding fill to sparse file failed: " << sparse.Rep();
1069
1070 RetCode ret = fb->Download(*sparse);
1071 // Two ways to handle it
1072 if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
1073 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
1074 << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
1075 << sparse.Rep();
1076 }
1077 ret = fb->Download(*sparse, true);
1078 if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
1079 EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
1080 << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
1081 << sparse.Rep();
1082 }
1083 }
1084
TEST_F(Fuzz,USBResetSpam)1085 TEST_F(Fuzz, USBResetSpam) {
1086 auto start = std::chrono::high_resolution_clock::now();
1087 std::chrono::duration<double> elapsed;
1088 int i = 0;
1089 do {
1090 ASSERT_EQ(transport->Reset(), 0) << "USB Reset failed after " << i << " resets in a row";
1091 elapsed = std::chrono::high_resolution_clock::now() - start;
1092 } while (i++, elapsed.count() < 5);
1093 std::string resp;
1094 EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
1095 << "getvar failed after " << i << " USB reset(s) in a row";
1096 }
1097
TEST_F(Fuzz,USBResetCommandSpam)1098 TEST_F(Fuzz, USBResetCommandSpam) {
1099 auto start = std::chrono::high_resolution_clock::now();
1100 std::chrono::duration<double> elapsed;
1101 do {
1102 std::string resp;
1103 std::vector<std::string> all;
1104 ASSERT_EQ(transport->Reset(), 0) << "USB Reset failed";
1105 EXPECT_EQ(fb->GetVarAll(&all), SUCCESS) << "getvar:all failed after USB reset";
1106 EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "getvar:product failed";
1107 elapsed = std::chrono::high_resolution_clock::now() - start;
1108 } while (elapsed.count() < 10);
1109 }
1110
TEST_F(Fuzz,USBResetAfterDownload)1111 TEST_F(Fuzz, USBResetAfterDownload) {
1112 std::vector<char> buf;
1113 buf.resize(1000000);
1114 EXPECT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Download command failed";
1115 EXPECT_EQ(transport->Reset(), 0) << "USB Reset failed";
1116 std::vector<std::string> all;
1117 EXPECT_EQ(fb->GetVarAll(&all), SUCCESS) << "getvar:all failed after USB reset.";
1118 }
1119
1120 // Getvar XML tests
TEST_P(ExtensionsGetVarConformance,VarExists)1121 TEST_P(ExtensionsGetVarConformance, VarExists) {
1122 std::string resp;
1123 EXPECT_EQ(fb->GetVar(GetParam().first, &resp), SUCCESS);
1124 }
1125
TEST_P(ExtensionsGetVarConformance,VarMatchesRegex)1126 TEST_P(ExtensionsGetVarConformance, VarMatchesRegex) {
1127 std::string resp;
1128 ASSERT_EQ(fb->GetVar(GetParam().first, &resp), SUCCESS);
1129 std::smatch sm;
1130 std::regex_match(resp, sm, GetParam().second.regex);
1131 EXPECT_FALSE(sm.empty()) << "The regex did not match";
1132 }
1133
1134 INSTANTIATE_TEST_CASE_P(XMLGetVar, ExtensionsGetVarConformance,
1135 ::testing::ValuesIn(GETVAR_XML_TESTS));
1136
TEST_P(AnyPartition,ReportedGetVarAll)1137 TEST_P(AnyPartition, ReportedGetVarAll) {
1138 // As long as the partition is reported in INFO, it would be tested by generic Conformance
1139 std::vector<std::tuple<std::string, uint64_t>> parts;
1140 ASSERT_EQ(fb->Partitions(&parts), SUCCESS) << "getvar:all failed";
1141 const std::string name = GetParam().first;
1142 if (GetParam().second.slots) {
1143 auto matcher = [&](const std::tuple<std::string, uint32_t>& tup) {
1144 return std::get<0>(tup) == name + "_a";
1145 };
1146 EXPECT_NE(std::find_if(parts.begin(), parts.end(), matcher), parts.end())
1147 << "partition '" + name + "_a' not reported in getvar:all";
1148 } else {
1149 auto matcher = [&](const std::tuple<std::string, uint32_t>& tup) {
1150 return std::get<0>(tup) == name;
1151 };
1152 EXPECT_NE(std::find_if(parts.begin(), parts.end(), matcher), parts.end())
1153 << "partition '" + name + "' not reported in getvar:all";
1154 }
1155 }
1156
TEST_P(AnyPartition,Hashable)1157 TEST_P(AnyPartition, Hashable) {
1158 const std::string name = GetParam().first;
1159 if (!config.checksum.empty()) { // We can use hash to validate
1160 for (const auto& part_name : real_parts) {
1161 // Get hash
1162 std::string hash;
1163 int retcode;
1164 std::string err_msg;
1165 if (GetParam().second.hashable) {
1166 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
1167 << err_msg;
1168 EXPECT_EQ(retcode, 0) << err_msg;
1169 } else { // Make sure it fails
1170 const std::string cmd = config.checksum + ' ' + part_name;
1171 EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
1172 << part_name + " is marked as non-hashable, but hashing did not fail";
1173 }
1174 }
1175 }
1176 }
1177
TEST_P(WriteablePartition,FlashCheck)1178 TEST_P(WriteablePartition, FlashCheck) {
1179 const std::string name = GetParam().first;
1180 auto part_info = GetParam().second;
1181
1182 for (const auto& part_name : real_parts) {
1183 std::vector<char> buf = RandomBuf(max_flash, rand_char);
1184 EXPECT_EQ(fb->FlashPartition(part_name, buf), part_info.parsed ? DEVICE_FAIL : SUCCESS)
1185 << "A partition with an image parsed by the bootloader should reject random "
1186 "garbage "
1187 "otherwise it should succeed";
1188 }
1189 }
1190
TEST_P(WriteablePartition,EraseCheck)1191 TEST_P(WriteablePartition, EraseCheck) {
1192 const std::string name = GetParam().first;
1193
1194 for (const auto& part_name : real_parts) {
1195 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1196 }
1197 }
1198
TEST_P(WriteHashNonParsedPartition,EraseZerosData)1199 TEST_P(WriteHashNonParsedPartition, EraseZerosData) {
1200 const std::string name = GetParam().first;
1201
1202 for (const auto& part_name : real_parts) {
1203 std::string err_msg;
1204 int retcode;
1205 const std::vector<char> buf = RandomBuf(max_flash, rand_char);
1206 // Partition is too big to write to entire thing
1207 // This can eventually be supported by using sparse images if too large
1208 if (max_flash < part_size) {
1209 std::string hash_before, hash_after;
1210 ASSERT_EQ(fb->FlashPartition(part_name, buf), SUCCESS);
1211 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1212 << err_msg;
1213 ASSERT_EQ(retcode, 0) << err_msg;
1214 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1215 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1216 << err_msg;
1217 ASSERT_EQ(retcode, 0) << err_msg;
1218 EXPECT_NE(hash_before, hash_after)
1219 << "The partition hash for " + part_name +
1220 " did not change after erasing a known value";
1221 } else {
1222 std::string hash_zeros, hash_ones, hash_middle, hash_after;
1223 const std::vector<char> buf_zeros(max_flash, 0);
1224 const std::vector<char> buf_ones(max_flash, -1); // All bits are set to 1
1225 ASSERT_EQ(fb->FlashPartition(part_name, buf_zeros), SUCCESS);
1226 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_zeros, &retcode, &err_msg))
1227 << err_msg;
1228 ASSERT_EQ(retcode, 0) << err_msg;
1229 ASSERT_EQ(fb->FlashPartition(part_name, buf_ones), SUCCESS);
1230 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_ones, &retcode, &err_msg))
1231 << err_msg;
1232 ASSERT_EQ(retcode, 0) << err_msg;
1233 ASSERT_NE(hash_zeros, hash_ones)
1234 << "Hashes of partion should not be the same when all bytes are 0xFF or 0x00";
1235 ASSERT_EQ(fb->FlashPartition(part_name, buf), SUCCESS);
1236 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_middle, &retcode, &err_msg))
1237 << err_msg;
1238 ASSERT_EQ(retcode, 0) << err_msg;
1239 ASSERT_NE(hash_zeros, hash_middle)
1240 << "Hashes of partion are the same when all bytes are 0x00 or test payload";
1241 ASSERT_NE(hash_ones, hash_middle)
1242 << "Hashes of partion are the same when all bytes are 0xFF or test payload";
1243 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1244 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1245 << err_msg;
1246 ASSERT_EQ(retcode, 0) << err_msg;
1247 EXPECT_TRUE(hash_zeros == hash_after || hash_ones == hash_after)
1248 << "Erasing " + part_name + " should set all the bytes to 0xFF or 0x00";
1249 }
1250 }
1251 }
1252
1253 // Only partitions that we can write and hash (name, fixture), TEST_P is (Fixture, test_name)
1254 INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteHashNonParsed, WriteHashNonParsedPartition,
1255 ::testing::ValuesIn(PARTITION_XML_WRITE_HASH_NONPARSED));
1256
1257 INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteHashable, WriteHashablePartition,
1258 ::testing::ValuesIn(PARTITION_XML_WRITE_HASHABLE));
1259
1260 // only partitions writeable
1261 INSTANTIATE_TEST_CASE_P(XMLPartitionsWriteable, WriteablePartition,
1262 ::testing::ValuesIn(PARTITION_XML_WRITEABLE));
1263
1264 // Every partition
1265 INSTANTIATE_TEST_CASE_P(XMLPartitionsAll, AnyPartition, ::testing::ValuesIn(PARTITION_XML_TESTS));
1266
1267 // Partition Fuzz tests
TEST_P(FuzzWriteablePartition,BoundsCheck)1268 TEST_P(FuzzWriteablePartition, BoundsCheck) {
1269 const std::string name = GetParam().first;
1270 auto part_info = GetParam().second;
1271
1272 for (const auto& part_name : real_parts) {
1273 // try and flash +1 too large, first erase and get a hash, make sure it does not change
1274 std::vector<char> buf = RandomBuf(max_flash + 1); // One too large
1275 if (part_info.hashable) {
1276 std::string hash_before, hash_after, err_msg;
1277 int retcode;
1278 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1279 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1280 << err_msg;
1281 ASSERT_EQ(retcode, 0) << err_msg;
1282 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1283 << "Flashing an image 1 byte too large to " + part_name + " did not fail";
1284 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1285 << err_msg;
1286 ASSERT_EQ(retcode, 0) << err_msg;
1287 EXPECT_EQ(hash_before, hash_after)
1288 << "Flashing too large of an image resulted in a changed partition hash for " +
1289 part_name;
1290 } else {
1291 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1292 << "Flashing an image 1 byte too large to " + part_name + " did not fail";
1293 }
1294 }
1295 }
1296
1297 INSTANTIATE_TEST_CASE_P(XMLFuzzPartitionsWriteable, FuzzWriteablePartition,
1298 ::testing::ValuesIn(PARTITION_XML_WRITEABLE));
1299
1300 // A parsed partition should have magic and such that is checked by the bootloader
1301 // Attempting to flash a random single byte should definately fail
TEST_P(FuzzWriteableParsedPartition,FlashGarbageImageSmall)1302 TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageSmall) {
1303 const std::string name = GetParam().first;
1304 auto part_info = GetParam().second;
1305
1306 for (const auto& part_name : real_parts) {
1307 std::vector<char> buf = RandomBuf(1);
1308 if (part_info.hashable) {
1309 std::string hash_before, hash_after, err_msg;
1310 int retcode;
1311 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1312 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1313 << err_msg;
1314 ASSERT_EQ(retcode, 0) << err_msg;
1315 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1316 << "A parsed partition should fail on a single byte";
1317 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1318 << err_msg;
1319 ASSERT_EQ(retcode, 0) << err_msg;
1320 EXPECT_EQ(hash_before, hash_after)
1321 << "Flashing a single byte to parsed partition " + part_name +
1322 " should fail and not change the partition hash";
1323 } else {
1324 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1325 << "Flashing a 1 byte image to a parsed partition should fail";
1326 }
1327 }
1328 }
1329
TEST_P(FuzzWriteableParsedPartition,FlashGarbageImageLarge)1330 TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge) {
1331 const std::string name = GetParam().first;
1332 auto part_info = GetParam().second;
1333
1334 for (const auto& part_name : real_parts) {
1335 std::vector<char> buf = RandomBuf(max_flash);
1336 if (part_info.hashable) {
1337 std::string hash_before, hash_after, err_msg;
1338 int retcode;
1339 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1340 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1341 << err_msg;
1342 ASSERT_EQ(retcode, 0) << err_msg;
1343 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1344 << "A parsed partition should not accept randomly generated images";
1345 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1346 << err_msg;
1347 ASSERT_EQ(retcode, 0) << err_msg;
1348 EXPECT_EQ(hash_before, hash_after)
1349 << "The hash of the partition has changed after attempting to flash garbage to "
1350 "a parsed partition";
1351 } else {
1352 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1353 << "A parsed partition should not accept randomly generated images";
1354 }
1355 }
1356 }
1357
TEST_P(FuzzWriteableParsedPartition,FlashGarbageImageLarge2)1358 TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge2) {
1359 const std::string name = GetParam().first;
1360 auto part_info = GetParam().second;
1361
1362 for (const auto& part_name : real_parts) {
1363 std::vector<char> buf(max_flash, -1); // All 1's
1364 if (part_info.hashable) {
1365 std::string hash_before, hash_after, err_msg;
1366 int retcode;
1367 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1368 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1369 << err_msg;
1370 ASSERT_EQ(retcode, 0) << err_msg;
1371 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1372 << "A parsed partition should not accept a image of all 0xFF";
1373 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1374 << err_msg;
1375 ASSERT_EQ(retcode, 0) << err_msg;
1376 EXPECT_EQ(hash_before, hash_after)
1377 << "The hash of the partition has changed after attempting to flash garbage to "
1378 "a parsed partition";
1379 } else {
1380 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1381 << "A parsed partition should not accept a image of all 0xFF";
1382 }
1383 }
1384 }
1385
TEST_P(FuzzWriteableParsedPartition,FlashGarbageImageLarge3)1386 TEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge3) {
1387 const std::string name = GetParam().first;
1388 auto part_info = GetParam().second;
1389
1390 for (const auto& part_name : real_parts) {
1391 std::vector<char> buf(max_flash, 0); // All 0's
1392 if (part_info.hashable) {
1393 std::string hash_before, hash_after, err_msg;
1394 int retcode;
1395 ASSERT_EQ(fb->Erase(part_name), SUCCESS) << "Erasing " + part_name + " failed";
1396 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))
1397 << err_msg;
1398 ASSERT_EQ(retcode, 0) << err_msg;
1399 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1400 << "A parsed partition should not accept a image of all 0x00";
1401 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))
1402 << err_msg;
1403 ASSERT_EQ(retcode, 0) << err_msg;
1404 EXPECT_EQ(hash_before, hash_after)
1405 << "The hash of the partition has changed after attempting to flash garbage to "
1406 "a parsed partition";
1407 } else {
1408 EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1409 << "A parsed partition should not accept a image of all 0x00";
1410 }
1411 }
1412 }
1413
1414 INSTANTIATE_TEST_CASE_P(XMLFuzzPartitionsWriteableParsed, FuzzWriteableParsedPartition,
1415 ::testing::ValuesIn(PARTITION_XML_WRITE_PARSED));
1416
1417 // Make sure all attempts to flash things are rejected
TEST_P(FuzzAnyPartitionLocked,RejectFlash)1418 TEST_P(FuzzAnyPartitionLocked, RejectFlash) {
1419 std::vector<char> buf = RandomBuf(5);
1420 for (const auto& part_name : real_parts) {
1421 ASSERT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)
1422 << "Flashing a partition should always fail in locked mode";
1423 }
1424 }
1425
1426 INSTANTIATE_TEST_CASE_P(XMLFuzzAnyPartitionLocked, FuzzAnyPartitionLocked,
1427 ::testing::ValuesIn(PARTITION_XML_TESTS));
1428
1429 // Test flashing unlock erases userdata
TEST_P(UserdataPartition,UnlockErases)1430 TEST_P(UserdataPartition, UnlockErases) {
1431 // Get hash after an erase
1432 int retcode;
1433 std::string err_msg, hash_before, hash_buf, hash_after;
1434 ASSERT_EQ(fb->Erase("userdata"), SUCCESS) << "Erasing uesrdata failed";
1435 ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_before, &retcode, &err_msg)) << err_msg;
1436 ASSERT_EQ(retcode, 0) << err_msg;
1437
1438 // Write garbage
1439 std::vector<char> buf = RandomBuf(max_flash / 2);
1440 ASSERT_EQ(fb->FlashPartition("userdata", buf), SUCCESS);
1441 ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_buf, &retcode, &err_msg)) << err_msg;
1442 ASSERT_EQ(retcode, 0) << err_msg;
1443
1444 // Validity check of hash
1445 EXPECT_NE(hash_before, hash_buf)
1446 << "Writing a random buffer to 'userdata' had the same hash as after erasing it";
1447 SetLockState(true); // Lock the device
1448
1449 SetLockState(false); // Unlock the device (should cause erase)
1450 ASSERT_TRUE(PartitionHash(fb.get(), "userdata", &hash_after, &retcode, &err_msg)) << err_msg;
1451 ASSERT_EQ(retcode, 0) << err_msg;
1452
1453 EXPECT_NE(hash_after, hash_buf) << "Unlocking the device did not cause the hash of userdata to "
1454 "change (i.e. it was not erased as required)";
1455 EXPECT_EQ(hash_after, hash_before) << "Unlocking the device did not produce the same hash of "
1456 "userdata as after doing an erase to userdata";
1457 }
1458
1459 // This is a hack to make this test disapeer if there is not a checsum, userdata is not hashable,
1460 // or userdata is not marked to be writeable in testing
1461 INSTANTIATE_TEST_CASE_P(XMLUserdataLocked, UserdataPartition,
1462 ::testing::ValuesIn(PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE));
1463
1464 // Packed images test
TEST_P(ExtensionsPackedValid,TestDeviceUnpack)1465 TEST_P(ExtensionsPackedValid, TestDeviceUnpack) {
1466 const std::string& packed_name = GetParam().first;
1467 const std::string& packed_image = GetParam().second.packed_img;
1468 const std::string& unpacked = GetParam().second.unpacked_dir;
1469
1470 // First we need to check for existence of images
1471 const extension::Configuration::PackedInfo& info = config.packed[packed_name];
1472
1473 const auto flash_part = [&](const std::string fname, const std::string part_name) {
1474 FILE* to_flash = fopen((SEARCH_PATH + fname).c_str(), "rb");
1475 ASSERT_NE(to_flash, nullptr) << "'" << fname << "'"
1476 << " failed to open for flashing";
1477 int fd = fileno(to_flash);
1478 size_t fsize = lseek(fd, 0, SEEK_END);
1479 ASSERT_GT(fsize, 0) << fname + " appears to be an empty image";
1480 ASSERT_EQ(fb->FlashPartition(part_name, fd, fsize), SUCCESS);
1481 fclose(to_flash);
1482 };
1483
1484 // We first need to set the slot count
1485 std::string var;
1486 int num_slots = 1;
1487 if (info.slots) {
1488 ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
1489 num_slots = strtol(var.c_str(), nullptr, 10);
1490 } else {
1491 for (const auto& part : info.children) {
1492 EXPECT_FALSE(config.partitions[part].slots)
1493 << "A partition can not have slots if the packed image does not";
1494 }
1495 }
1496
1497 for (int i = 0; i < num_slots; i++) {
1498 std::unordered_map<std::string, std::string> initial_hashes;
1499 const std::string packed_suffix =
1500 info.slots ? android::base::StringPrintf("_%c", 'a' + i) : "";
1501
1502 // Flash the paritions manually and get hash
1503 for (const auto& part : info.children) {
1504 const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
1505 const std::string suffix = part_info.slots ? packed_suffix : "";
1506 const std::string part_name = part + suffix;
1507
1508 ASSERT_EQ(fb->Erase(part_name), SUCCESS);
1509 const std::string fpath = unpacked + '/' + part + ".img";
1510 ASSERT_NO_FATAL_FAILURE(flash_part(fpath, part_name))
1511 << "Failed to flash '" + fpath + "'";
1512 // If the partition is hashable we store it
1513 if (part_info.hashable) {
1514 std::string hash, err_msg;
1515 int retcode;
1516 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
1517 << err_msg;
1518 ASSERT_EQ(retcode, 0) << err_msg;
1519 initial_hashes[part] = hash;
1520 }
1521 }
1522
1523 // erase once at the end, to avoid false positives if flashing does nothing
1524 for (const auto& part : info.children) {
1525 const std::string suffix = config.partitions[part].slots ? packed_suffix : "";
1526 ASSERT_EQ(fb->Erase(part + suffix), SUCCESS);
1527 }
1528
1529 // Now we flash the packed image and compare our hashes
1530 ASSERT_NO_FATAL_FAILURE(flash_part(packed_image, packed_name + packed_suffix));
1531
1532 for (const auto& part : info.children) {
1533 const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
1534 // If the partition is hashable we check it
1535 if (part_info.hashable) {
1536 const std::string suffix = part_info.slots ? packed_suffix : "";
1537 const std::string part_name = part + suffix;
1538 std::string hash, err_msg;
1539 int retcode;
1540 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
1541 << err_msg;
1542 ASSERT_EQ(retcode, 0) << err_msg;
1543 std::string msg =
1544 "The hashes between flashing the packed image and directly flashing '" +
1545 part_name + "' does not match";
1546 EXPECT_EQ(hash, initial_hashes[part]) << msg;
1547 }
1548 }
1549 }
1550 }
1551
1552 INSTANTIATE_TEST_CASE_P(XMLTestPacked, ExtensionsPackedValid,
1553 ::testing::ValuesIn(PACKED_XML_SUCCESS_TESTS));
1554
1555 // Packed images test
TEST_P(ExtensionsPackedInvalid,TestDeviceUnpack)1556 TEST_P(ExtensionsPackedInvalid, TestDeviceUnpack) {
1557 const std::string& packed_name = GetParam().first;
1558 const std::string& packed_image = GetParam().second.packed_img;
1559
1560 // First we need to check for existence of images
1561 const extension::Configuration::PackedInfo& info = config.packed[packed_name];
1562
1563 // We first need to set the slot count
1564 std::string var;
1565 int num_slots = 1;
1566 if (info.slots) {
1567 ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
1568 num_slots = strtol(var.c_str(), nullptr, 10);
1569 } else {
1570 for (const auto& part : info.children) {
1571 EXPECT_FALSE(config.partitions[part].slots)
1572 << "A partition can not have slots if the packed image does not";
1573 }
1574 }
1575
1576 for (int i = 0; i < num_slots; i++) {
1577 std::unordered_map<std::string, std::string> initial_hashes;
1578 const std::string packed_suffix =
1579 info.slots ? android::base::StringPrintf("_%c", 'a' + i) : "";
1580
1581 // manually and get hash
1582 for (const auto& part : info.children) {
1583 const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
1584 const std::string suffix = part_info.slots ? packed_suffix : "";
1585 const std::string part_name = part + suffix;
1586
1587 // If the partition is hashable we store it
1588 if (part_info.hashable) {
1589 std::string hash, err_msg;
1590 int retcode;
1591 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
1592 << err_msg;
1593 ASSERT_EQ(retcode, 0) << err_msg;
1594 initial_hashes[part] = hash;
1595 }
1596 }
1597
1598 // Attempt to flash the invalid file
1599 FILE* to_flash = fopen((SEARCH_PATH + packed_image).c_str(), "rb");
1600 ASSERT_NE(to_flash, nullptr) << "'" << packed_image << "'"
1601 << " failed to open for flashing";
1602 int fd = fileno(to_flash);
1603 size_t fsize = lseek(fd, 0, SEEK_END);
1604 ASSERT_GT(fsize, 0) << packed_image + " appears to be an empty image";
1605 ASSERT_EQ(fb->FlashPartition(packed_name + packed_suffix, fd, fsize), DEVICE_FAIL)
1606 << "Expected flashing to fail for " + packed_image;
1607 fclose(to_flash);
1608
1609 for (const auto& part : info.children) {
1610 const extension::Configuration::PartitionInfo& part_info = config.partitions[part];
1611 // If the partition is hashable we check it
1612 if (part_info.hashable) {
1613 const std::string suffix = part_info.slots ? packed_suffix : "";
1614 const std::string part_name = part + suffix;
1615 std::string hash, err_msg;
1616 int retcode;
1617 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))
1618 << err_msg;
1619 ASSERT_EQ(retcode, 0) << err_msg;
1620 std::string msg = "Flashing an invalid image changed the hash of '" + part_name;
1621 EXPECT_EQ(hash, initial_hashes[part]) << msg;
1622 }
1623 }
1624 }
1625 }
1626
1627 INSTANTIATE_TEST_CASE_P(XMLTestPacked, ExtensionsPackedInvalid,
1628 ::testing::ValuesIn(PACKED_XML_FAIL_TESTS));
1629
1630 // OEM xml tests
TEST_P(ExtensionsOemConformance,RunOEMTest)1631 TEST_P(ExtensionsOemConformance, RunOEMTest) {
1632 const std::string& cmd = std::get<0>(GetParam());
1633 // bool restricted = std::get<1>(GetParam());
1634 const extension::Configuration::CommandTest& test = std::get<2>(GetParam());
1635
1636 const RetCode expect = (test.expect == extension::FAIL) ? DEVICE_FAIL : SUCCESS;
1637
1638 // Does the test require staging something?
1639 if (!test.input.empty()) { // Non-empty string
1640 FILE* to_stage = fopen((SEARCH_PATH + test.input).c_str(), "rb");
1641 ASSERT_NE(to_stage, nullptr) << "'" << test.input << "'"
1642 << " failed to open for staging";
1643 int fd = fileno(to_stage);
1644 size_t fsize = lseek(fd, 0, SEEK_END);
1645 std::string var;
1646 EXPECT_EQ(fb->GetVar("max-download-size", &var), SUCCESS);
1647 int64_t size = strtoll(var.c_str(), nullptr, 16);
1648 EXPECT_LT(fsize, size) << "'" << test.input << "'"
1649 << " is too large for staging";
1650 ASSERT_EQ(fb->Download(fd, fsize), SUCCESS) << "'" << test.input << "'"
1651 << " failed to download for staging";
1652 fclose(to_stage);
1653 }
1654 // Run the command
1655 int dsize = -1;
1656 std::string resp;
1657 const std::string full_cmd = "oem " + cmd + " " + test.arg;
1658 ASSERT_EQ(fb->RawCommand(full_cmd, &resp, nullptr, &dsize), expect);
1659
1660 // This is how we test if indeed data response
1661 if (test.expect == extension::DATA) {
1662 EXPECT_GT(dsize, 0);
1663 }
1664
1665 // Validate response if neccesary
1666 if (!test.regex_str.empty()) {
1667 std::smatch sm;
1668 std::regex_match(resp, sm, test.regex);
1669 EXPECT_FALSE(sm.empty()) << "The oem regex did not match";
1670 }
1671
1672 // If payload, we validate that as well
1673 const std::vector<std::string> args = SplitBySpace(test.validator);
1674 if (args.size()) {
1675 // Save output
1676 const std::string save_loc =
1677 OUTPUT_PATH + (test.output.empty() ? DEFAULT_OUPUT_NAME : test.output);
1678 std::string resp;
1679 ASSERT_EQ(fb->Upload(save_loc, &resp), SUCCESS)
1680 << "Saving output file failed with (" << fb->Error() << ") " << resp;
1681 // Build the arguments to the validator
1682 std::vector<std::string> prog_args(args.begin() + 1, args.end());
1683 prog_args.push_back(full_cmd); // Pass in the full command
1684 prog_args.push_back(save_loc); // Pass in the save location
1685 // Run the validation program
1686 int pipe;
1687 const pid_t pid = StartProgram(args[0], prog_args, &pipe);
1688 ASSERT_GT(pid, 0) << "Failed to launch validation program: " << args[0];
1689 std::string error_msg;
1690 int ret = WaitProgram(pid, pipe, &error_msg);
1691 EXPECT_EQ(ret, 0) << error_msg; // Program exited correctly
1692 }
1693 }
1694
1695 INSTANTIATE_TEST_CASE_P(XMLOEM, ExtensionsOemConformance, ::testing::ValuesIn(OEM_XML_TESTS));
1696
1697 // Sparse Tests
TEST_P(SparseTestPartition,SparseSingleBlock)1698 TEST_P(SparseTestPartition, SparseSingleBlock) {
1699 const std::string name = GetParam().first;
1700 auto part_info = GetParam().second;
1701 const std::string part_name = name + (part_info.slots ? "_a" : "");
1702 SparseWrapper sparse(4096, 4096);
1703 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
1704 std::vector<char> buf = RandomBuf(4096);
1705 ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
1706 << "Adding data failed to sparse file: " << sparse.Rep();
1707
1708 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
1709 EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
1710 std::string hash, hash_new, err_msg;
1711 int retcode;
1712 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
1713 ASSERT_EQ(retcode, 0) << err_msg;
1714 // Now flash it the non-sparse way
1715 EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
1716 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
1717 ASSERT_EQ(retcode, 0) << err_msg;
1718
1719 EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
1720 "methods did not result in the same hash";
1721 }
1722
TEST_P(SparseTestPartition,SparseFill)1723 TEST_P(SparseTestPartition, SparseFill) {
1724 const std::string name = GetParam().first;
1725 auto part_info = GetParam().second;
1726 const std::string part_name = name + (part_info.slots ? "_a" : "");
1727 int64_t size = (max_dl / 4096) * 4096;
1728 SparseWrapper sparse(4096, size);
1729 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
1730 ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size, 0), 0)
1731 << "Adding data failed to sparse file: " << sparse.Rep();
1732
1733 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
1734 EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
1735 std::string hash, hash_new, err_msg;
1736 int retcode;
1737 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
1738 ASSERT_EQ(retcode, 0) << err_msg;
1739 // Now flash it the non-sparse way
1740 std::vector<char> buf(size);
1741 for (auto iter = buf.begin(); iter < buf.end(); iter += 4) {
1742 iter[0] = 0xef;
1743 iter[1] = 0xbe;
1744 iter[2] = 0xad;
1745 iter[3] = 0xde;
1746 }
1747 EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
1748 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
1749 ASSERT_EQ(retcode, 0) << err_msg;
1750
1751 EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
1752 "methods did not result in the same hash";
1753 }
1754
1755 // This tests to make sure it does not overwrite previous flashes
TEST_P(SparseTestPartition,SparseMultiple)1756 TEST_P(SparseTestPartition, SparseMultiple) {
1757 const std::string name = GetParam().first;
1758 auto part_info = GetParam().second;
1759 const std::string part_name = name + (part_info.slots ? "_a" : "");
1760 int64_t size = (max_dl / 4096) * 4096;
1761 SparseWrapper sparse(4096, size / 2);
1762 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
1763 ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size / 2, 0), 0)
1764 << "Adding data failed to sparse file: " << sparse.Rep();
1765 EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
1766 EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
1767
1768 SparseWrapper sparse2(4096, size / 2);
1769 ASSERT_TRUE(*sparse) << "Sparse image creation failed";
1770 std::vector<char> buf = RandomBuf(size / 2);
1771 ASSERT_EQ(sparse_file_add_data(*sparse2, buf.data(), buf.size(), (size / 2) / 4096), 0)
1772 << "Adding data failed to sparse file: " << sparse2.Rep();
1773 EXPECT_EQ(fb->Download(*sparse2), SUCCESS) << "Download sparse failed: " << sparse2.Rep();
1774 EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse2.Rep();
1775
1776 std::string hash, hash_new, err_msg;
1777 int retcode;
1778 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
1779 ASSERT_EQ(retcode, 0) << err_msg;
1780 // Now flash it the non-sparse way
1781 std::vector<char> fbuf(size);
1782 for (auto iter = fbuf.begin(); iter < fbuf.begin() + size / 2; iter += 4) {
1783 iter[0] = 0xef;
1784 iter[1] = 0xbe;
1785 iter[2] = 0xad;
1786 iter[3] = 0xde;
1787 }
1788 fbuf.assign(buf.begin(), buf.end());
1789 EXPECT_EQ(fb->FlashPartition(part_name, fbuf), SUCCESS) << "Flashing image failed: ";
1790 ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
1791 ASSERT_EQ(retcode, 0) << err_msg;
1792
1793 EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
1794 "methods did not result in the same hash";
1795 }
1796
1797 INSTANTIATE_TEST_CASE_P(XMLSparseTest, SparseTestPartition,
1798 ::testing::ValuesIn(SINGLE_PARTITION_XML_WRITE_HASHABLE));
1799
GenerateXmlTests(const extension::Configuration & config)1800 void GenerateXmlTests(const extension::Configuration& config) {
1801 // Build the getvar tests
1802 for (const auto& it : config.getvars) {
1803 GETVAR_XML_TESTS.push_back(std::make_pair(it.first, it.second));
1804 }
1805
1806 // Build the partition tests, to interface with gtest we need to do it this way
1807 for (const auto& it : config.partitions) {
1808 const auto tup = std::make_tuple(it.first, it.second);
1809 PARTITION_XML_TESTS.push_back(tup); // All partitions
1810
1811 if (it.second.test == it.second.YES) {
1812 PARTITION_XML_WRITEABLE.push_back(tup); // All writeable partitions
1813
1814 if (it.second.hashable) {
1815 PARTITION_XML_WRITE_HASHABLE.push_back(tup); // All write and hashable
1816 if (!it.second.parsed) {
1817 PARTITION_XML_WRITE_HASH_NONPARSED.push_back(
1818 tup); // All write hashed and non-parsed
1819 }
1820 }
1821 if (it.second.parsed) {
1822 PARTITION_XML_WRITE_PARSED.push_back(tup); // All write and parsed
1823 }
1824 }
1825 }
1826
1827 // Build the packed tests, only useful if we have a hash
1828 if (!config.checksum.empty()) {
1829 for (const auto& it : config.packed) {
1830 for (const auto& test : it.second.tests) {
1831 const auto tup = std::make_tuple(it.first, test);
1832 if (test.expect == extension::OKAY) { // only testing the success case
1833 PACKED_XML_SUCCESS_TESTS.push_back(tup);
1834 } else {
1835 PACKED_XML_FAIL_TESTS.push_back(tup);
1836 }
1837 }
1838 }
1839 }
1840
1841 // This is a hack to make this test disapeer if there is not a checksum, userdata is not
1842 // hashable, or userdata is not marked to be writeable in testing
1843 const auto part_info = config.partitions.find("userdata");
1844 if (!config.checksum.empty() && part_info != config.partitions.end() &&
1845 part_info->second.hashable &&
1846 part_info->second.test == extension::Configuration::PartitionInfo::YES) {
1847 PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE.push_back(
1848 std::make_tuple(part_info->first, part_info->second));
1849 }
1850
1851 if (!PARTITION_XML_WRITE_HASHABLE.empty()) {
1852 SINGLE_PARTITION_XML_WRITE_HASHABLE.push_back(PARTITION_XML_WRITE_HASHABLE.front());
1853 }
1854
1855 // Build oem tests
1856 for (const auto& it : config.oem) {
1857 auto oem_cmd = it.second;
1858 for (const auto& t : oem_cmd.tests) {
1859 OEM_XML_TESTS.push_back(std::make_tuple(it.first, oem_cmd.restricted, t));
1860 }
1861 }
1862 }
1863
1864 } // namespace fastboot
1865
main(int argc,char ** argv)1866 int main(int argc, char** argv) {
1867 std::string err;
1868 // Parse the args
1869 const std::unordered_map<std::string, std::string> args = fastboot::ParseArgs(argc, argv, &err);
1870 if (!err.empty()) {
1871 printf("%s\n", err.c_str());
1872 return -1;
1873 }
1874
1875 if (args.find("config") != args.end()) {
1876 auto found = args.find("search_path");
1877 fastboot::SEARCH_PATH = (found != args.end()) ? found->second + "/" : "";
1878 found = args.find("output_path");
1879 fastboot::OUTPUT_PATH = (found != args.end()) ? found->second + "/" : "/tmp/";
1880 if (!fastboot::extension::ParseXml(fastboot::SEARCH_PATH + args.at("config"),
1881 &fastboot::config)) {
1882 printf("XML config parsing failed\n");
1883 return -1;
1884 }
1885 // To interface with gtest, must set global scope test variables
1886 fastboot::GenerateXmlTests(fastboot::config);
1887 }
1888
1889 if (args.find("serial") != args.end()) {
1890 fastboot::FastBootTest::device_serial = args.at("serial");
1891 }
1892
1893 setbuf(stdout, NULL); // no buffering
1894
1895 if (!fastboot::FastBootTest::IsFastbootOverTcp()) {
1896 printf("<Waiting for Device>\n");
1897 const auto matcher = [](usb_ifc_info* info) -> int {
1898 return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
1899 };
1900 Transport* transport = nullptr;
1901 while (!transport) {
1902 transport = usb_open(matcher);
1903 std::this_thread::sleep_for(std::chrono::milliseconds(10));
1904 }
1905 transport->Close();
1906 }
1907
1908 if (args.find("serial_port") != args.end()) {
1909 fastboot::FastBootTest::serial_port = fastboot::ConfigureSerial(args.at("serial_port"));
1910 }
1911
1912 ::testing::InitGoogleTest(&argc, argv);
1913 auto ret = RUN_ALL_TESTS();
1914 if (fastboot::FastBootTest::serial_port > 0) {
1915 close(fastboot::FastBootTest::serial_port);
1916 }
1917 return ret;
1918 }
1919