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", &current_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", &current_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