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