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 <termios.h>
36 #include <unistd.h>
37 #include <chrono>
38 #include <cstdlib>
39 #include <fstream>
40 #include <map>
41 #include <random>
42 #include <regex>
43 #include <set>
44 #include <thread>
45 #include <vector>
46 
47 #include <android-base/stringprintf.h>
48 #include <android-base/strings.h>
49 #include <gtest/gtest.h>
50 
51 #include "fastboot_driver.h"
52 #include "tcp.h"
53 #include "usb.h"
54 
55 #include "extensions.h"
56 #include "fixtures.h"
57 #include "test_utils.h"
58 #include "transport_sniffer.h"
59 
60 using namespace std::literals::chrono_literals;
61 
62 namespace fastboot {
63 
MatchFastboot(usb_ifc_info * info,const std::string & local_serial)64 int FastBootTest::MatchFastboot(usb_ifc_info* info, const std::string& local_serial) {
65     if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {
66         return -1;
67     }
68 
69     cb_scratch = info->device_path;
70 
71     // require matching serial number or device path if requested
72     // at the command line with the -s option.
73     if (!local_serial.empty() && local_serial != info->serial_number &&
74         local_serial != info->device_path)
75         return -1;
76     return 0;
77 }
78 
IsFastbootOverTcp()79 bool FastBootTest::IsFastbootOverTcp() {
80     return android::base::StartsWith(device_serial, "tcp:");
81 }
82 
UsbStillAvailible()83 bool FastBootTest::UsbStillAvailible() {
84     if (IsFastbootOverTcp()) return true;
85 
86     // For some reason someone decided to prefix the path with "usb:"
87     std::string prefix("usb:");
88     if (std::equal(prefix.begin(), prefix.end(), device_path.begin())) {
89         std::string fname(device_path.begin() + prefix.size(), device_path.end());
90         std::string real_path =
91                 android::base::StringPrintf("/sys/bus/usb/devices/%s/serial", fname.c_str());
92         std::ifstream f(real_path.c_str());
93         return f.good();
94     }
95     exit(-1);  // This should never happen
96     return true;
97 }
98 
UserSpaceFastboot()99 bool FastBootTest::UserSpaceFastboot() {
100     std::string value;
101     fb->GetVar("is-userspace", &value);
102     return value == "yes";
103 }
104 
DownloadCommand(uint32_t size,std::string * response,std::vector<std::string> * info)105 RetCode FastBootTest::DownloadCommand(uint32_t size, std::string* response,
106                                       std::vector<std::string>* info) {
107     return fb->DownloadCommand(size, response, info);
108 }
109 
SendBuffer(const std::vector<char> & buf)110 RetCode FastBootTest::SendBuffer(const std::vector<char>& buf) {
111     return fb->SendBuffer(buf);
112 }
113 
HandleResponse(std::string * response,std::vector<std::string> * info,int * dsize)114 RetCode FastBootTest::HandleResponse(std::string* response, std::vector<std::string>* info,
115                                      int* dsize) {
116     return fb->HandleResponse(response, info, dsize);
117 }
118 
SetUp()119 void FastBootTest::SetUp() {
120     if (device_path != "") {               // make sure the device is still connected
121         ASSERT_TRUE(UsbStillAvailible());  // The device disconnected
122     }
123 
124     if (IsFastbootOverTcp()) {
125         ConnectTcpFastbootDevice();
126     } else {
127         const auto matcher = [](usb_ifc_info* info) -> int {
128             return MatchFastboot(info, device_serial);
129         };
130         for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
131             std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
132             if (usb)
133                 transport = std::unique_ptr<TransportSniffer>(
134                         new TransportSniffer(std::move(usb), serial_port));
135             std::this_thread::sleep_for(std::chrono::milliseconds(10));
136         }
137     }
138 
139     ASSERT_TRUE(transport);  // no nullptr
140 
141     if (device_path == "") {  // We set it the first time, then make sure it never changes
142         device_path = cb_scratch;
143     } else {
144         ASSERT_EQ(device_path, cb_scratch);  // The path can not change
145     }
146     fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
147     // No error checking since non-A/B devices may not support the command
148     fb->GetVar("current-slot", &initial_slot);
149 }
150 
TearDown()151 void FastBootTest::TearDown() {
152     EXPECT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
153     // No error checking since non-A/B devices may not support the command
154     fb->SetActive(initial_slot);
155 
156     TearDownSerial();
157 
158     fb.reset();
159 
160     if (transport) {
161         transport.reset();
162     }
163 
164     ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
165 }
166 
167 // TODO, this should eventually be piped to a file instead of stdout
TearDownSerial()168 void FastBootTest::TearDownSerial() {
169     if (IsFastbootOverTcp()) return;
170 
171     if (!transport) return;
172     // One last read from serial
173     transport->ProcessSerial();
174     if (HasFailure()) {
175         // TODO, print commands leading up
176         printf("<<<<<<<< TRACE BEGIN >>>>>>>>>\n");
177         printf("%s", transport->CreateTrace().c_str());
178         printf("<<<<<<<< TRACE END >>>>>>>>>\n");
179         // std::vector<std::pair<const TransferType, const std::vector<char>>>  prev =
180         // transport->Transfers();
181     }
182 }
183 
ConnectTcpFastbootDevice()184 void FastBootTest::ConnectTcpFastbootDevice() {
185     for (int i = 0; i < MAX_TCP_TRIES && !transport; i++) {
186         std::string error;
187         std::unique_ptr<Transport> tcp(
188                 tcp::Connect(device_serial.substr(4), tcp::kDefaultPort, &error).release());
189         if (tcp)
190             transport = std::unique_ptr<TransportSniffer>(new TransportSniffer(std::move(tcp), 0));
191         if (transport != nullptr) break;
192         std::this_thread::sleep_for(std::chrono::milliseconds(10));
193     }
194 }
195 
ReconnectFastbootDevice()196 void FastBootTest::ReconnectFastbootDevice() {
197     fb.reset();
198     transport.reset();
199 
200     if (IsFastbootOverTcp()) {
201         ConnectTcpFastbootDevice();
202         device_path = cb_scratch;
203         fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
204         return;
205     }
206 
207     while (UsbStillAvailible())
208         ;
209     printf("WAITING FOR DEVICE\n");
210     // Need to wait for device
211     const auto matcher = [](usb_ifc_info* info) -> int {
212         return MatchFastboot(info, device_serial);
213     };
214     while (!transport) {
215         std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
216         if (usb) {
217             transport = std::unique_ptr<TransportSniffer>(
218                     new TransportSniffer(std::move(usb), serial_port));
219         }
220         std::this_thread::sleep_for(1s);
221     }
222     device_path = cb_scratch;
223     fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
224 }
225 
SetLockState(bool unlock,bool assert_change)226 void FastBootTest::SetLockState(bool unlock, bool assert_change) {
227     if (!fb) {
228         return;
229     }
230 
231     // User space fastboot implementations are not allowed to communicate to
232     // secure hardware and hence cannot lock/unlock the device.
233     if (UserSpaceFastboot()) {
234         return;
235     }
236 
237     std::string resp;
238     std::vector<std::string> info;
239     // To avoid risk of bricking device, make sure unlock ability is set to 1
240     ASSERT_EQ(fb->RawCommand("flashing get_unlock_ability", &resp, &info), SUCCESS)
241             << "'flashing get_unlock_ability' failed";
242 
243     // There are two ways this can be reported, through info or the actual response
244     if (!resp.empty()) {  // must be in the info response
245         ASSERT_EQ(resp.back(), '1')
246                 << "Unlock ability must be set to 1 to avoid bricking device, see "
247                    "'https://source.android.com/devices/bootloader/unlock-trusty'";
248     } else {
249         ASSERT_FALSE(info.empty()) << "'flashing get_unlock_ability' returned empty response";
250         ASSERT_FALSE(info.back().empty()) << "Expected non-empty info response";
251         ASSERT_EQ(info.back().back(), '1')
252                 << "Unlock ability must be set to 1 to avoid bricking device, see "
253                    "'https://source.android.com/devices/bootloader/unlock-trusty'";
254     }
255 
256     EXPECT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
257     ASSERT_TRUE(resp == "no" || resp == "yes")
258             << "getvar:unlocked response was not 'no' or 'yes': " + resp;
259 
260     if ((unlock && resp == "no") || (!unlock && resp == "yes")) {
261         std::string cmd = unlock ? "unlock" : "lock";
262         ASSERT_EQ(fb->RawCommand("flashing " + cmd, &resp), SUCCESS)
263                 << "Attempting to change locked state, but 'flashing" + cmd + "' command failed";
264         printf("PLEASE RESPOND TO PROMPT FOR '%sing' BOOTLOADER ON DEVICE\n", cmd.c_str());
265         ReconnectFastbootDevice();
266         if (assert_change) {
267             ASSERT_EQ(fb->GetVar("unlocked", &resp), SUCCESS) << "getvar:unlocked failed";
268             ASSERT_EQ(resp, unlock ? "yes" : "no")
269                     << "getvar:unlocked response was not 'no' or 'yes': " + resp;
270         }
271         printf("SUCCESS\n");
272     }
273 }
274 
275 std::string FastBootTest::device_path = "";
276 std::string FastBootTest::cb_scratch = "";
277 std::string FastBootTest::initial_slot = "";
278 int FastBootTest::serial_port = 0;
279 std::string FastBootTest::device_serial = "";
280 
281 template <bool UNLOCKED>
SetUp()282 void ModeTest<UNLOCKED>::SetUp() {
283     ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());
284     ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));
285 }
286 // Need to instatiate it, so linker can find it later
287 template class ModeTest<true>;
288 template class ModeTest<false>;
289 
TearDown()290 void Fuzz::TearDown() {
291     ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
292 
293     TearDownSerial();
294 
295     std::string tmp;
296     if (fb->GetVar("product", &tmp) != SUCCESS) {
297         printf("DEVICE UNRESPONSE, attempting to recover...");
298         transport->Reset();
299         printf("issued USB reset...");
300 
301         if (fb->GetVar("product", &tmp) != SUCCESS) {
302             printf("FAIL\n");
303             exit(-1);
304         }
305         printf("SUCCESS!\n");
306     }
307 
308     if (transport) {
309         transport.reset();
310     }
311 
312     ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
313 }
314 
315 template <bool UNLOCKED>
SetUp()316 void ExtensionsPartition<UNLOCKED>::SetUp() {
317     ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());
318     ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));
319 
320     if (!fb) {
321         return;
322     }
323     const std::string name = GetParam().first;
324 
325     std::string var;
326     ASSERT_EQ(fb->GetVar("slot-count", &var), SUCCESS) << "Getting slot count failed";
327     int32_t num_slots = strtol(var.c_str(), nullptr, 10);
328     real_parts = GeneratePartitionNames(name, GetParam().second.slots ? num_slots : 0);
329 
330     ASSERT_EQ(fb->GetVar("partition-size:" + real_parts.front(), &var), SUCCESS)
331             << "Getting partition size failed";
332     part_size = strtoll(var.c_str(), nullptr, 16);
333     ASSERT_GT(part_size, 0) << "Partition size reported was invalid";
334 
335     ASSERT_EQ(fb->GetVar("max-download-size", &var), SUCCESS) << "Getting max download size failed";
336     max_dl = strtoll(var.c_str(), nullptr, 16);
337     ASSERT_GT(max_dl, 0) << "Max download size reported was invalid";
338 
339     max_flash = std::min(part_size, max_dl);
340 }
341 template class ExtensionsPartition<true>;
342 template class ExtensionsPartition<false>;
343 
344 }  // end namespace fastboot
345