Add more checking for fastboot to detect malformed
requests.
Such as checking no control characters in the command
send from host.
Make sure the download command length is eight bytes.
And report FAIL if download length is zero.
Test: adb reboot fastboot
fuzzy_fastboot --gtest_filter=Fuzz.DownloadInvalid1
fuzzy_fastboot --gtest_filter=Fuzz.DownloadInvalid2
fuzzy_fastboot --gtest_filter=Fuzz.DownloadInvalid7
fuzzy_fastboot --gtest_filter=Fuzz.DownloadInvalid8
Bug: 212628476
Change-Id: I750174205377395b5328923fb00462d078f3310d
229 lines
8.1 KiB
C++
229 lines
8.1 KiB
C++
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "fastboot_device.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <android-base/logging.h>
|
|
#include <android-base/properties.h>
|
|
#include <android-base/strings.h>
|
|
#include <android/binder_manager.h>
|
|
#include <android/hardware/boot/1.0/IBootControl.h>
|
|
#include <android/hardware/fastboot/1.1/IFastboot.h>
|
|
#include <fs_mgr.h>
|
|
#include <fs_mgr/roots.h>
|
|
#include <health-shim/shim.h>
|
|
#include <healthhalutils/HealthHalUtils.h>
|
|
|
|
#include "constants.h"
|
|
#include "flashing.h"
|
|
#include "tcp_client.h"
|
|
#include "usb_client.h"
|
|
|
|
using std::string_literals::operator""s;
|
|
using android::fs_mgr::EnsurePathUnmounted;
|
|
using android::fs_mgr::Fstab;
|
|
using ::android::hardware::hidl_string;
|
|
using ::android::hardware::boot::V1_0::IBootControl;
|
|
using ::android::hardware::boot::V1_0::Slot;
|
|
using ::android::hardware::fastboot::V1_1::IFastboot;
|
|
|
|
namespace sph = std::placeholders;
|
|
|
|
std::shared_ptr<aidl::android::hardware::health::IHealth> get_health_service() {
|
|
using aidl::android::hardware::health::IHealth;
|
|
using HidlHealth = android::hardware::health::V2_0::IHealth;
|
|
using aidl::android::hardware::health::HealthShim;
|
|
auto service_name = IHealth::descriptor + "/default"s;
|
|
if (AServiceManager_isDeclared(service_name.c_str())) {
|
|
ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str()));
|
|
std::shared_ptr<IHealth> health = IHealth::fromBinder(binder);
|
|
if (health != nullptr) return health;
|
|
LOG(WARNING) << "AIDL health service is declared, but it cannot be retrieved.";
|
|
}
|
|
LOG(INFO) << "Unable to get AIDL health service, trying HIDL...";
|
|
android::sp<HidlHealth> hidl_health = android::hardware::health::V2_0::get_health_service();
|
|
if (hidl_health != nullptr) {
|
|
return ndk::SharedRefBase::make<HealthShim>(hidl_health);
|
|
}
|
|
LOG(WARNING) << "No health implementation is found.";
|
|
return nullptr;
|
|
}
|
|
|
|
FastbootDevice::FastbootDevice()
|
|
: kCommandMap({
|
|
{FB_CMD_SET_ACTIVE, SetActiveHandler},
|
|
{FB_CMD_DOWNLOAD, DownloadHandler},
|
|
{FB_CMD_GETVAR, GetVarHandler},
|
|
{FB_CMD_SHUTDOWN, ShutDownHandler},
|
|
{FB_CMD_REBOOT, RebootHandler},
|
|
{FB_CMD_REBOOT_BOOTLOADER, RebootBootloaderHandler},
|
|
{FB_CMD_REBOOT_FASTBOOT, RebootFastbootHandler},
|
|
{FB_CMD_REBOOT_RECOVERY, RebootRecoveryHandler},
|
|
{FB_CMD_ERASE, EraseHandler},
|
|
{FB_CMD_FLASH, FlashHandler},
|
|
{FB_CMD_CREATE_PARTITION, CreatePartitionHandler},
|
|
{FB_CMD_DELETE_PARTITION, DeletePartitionHandler},
|
|
{FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
|
|
{FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
|
|
{FB_CMD_OEM, OemCmdHandler},
|
|
{FB_CMD_GSI, GsiHandler},
|
|
{FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
|
|
{FB_CMD_FETCH, FetchHandler},
|
|
}),
|
|
boot_control_hal_(IBootControl::getService()),
|
|
health_hal_(get_health_service()),
|
|
fastboot_hal_(IFastboot::getService()),
|
|
active_slot_("") {
|
|
if (android::base::GetProperty("fastbootd.protocol", "usb") == "tcp") {
|
|
transport_ = std::make_unique<ClientTcpTransport>();
|
|
} else {
|
|
transport_ = std::make_unique<ClientUsbTransport>();
|
|
}
|
|
|
|
if (boot_control_hal_) {
|
|
boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
|
|
}
|
|
|
|
// Make sure cache is unmounted, since recovery will have mounted it for
|
|
// logging.
|
|
Fstab fstab;
|
|
if (ReadDefaultFstab(&fstab)) {
|
|
EnsurePathUnmounted(&fstab, "/cache");
|
|
}
|
|
}
|
|
|
|
FastbootDevice::~FastbootDevice() {
|
|
CloseDevice();
|
|
}
|
|
|
|
void FastbootDevice::CloseDevice() {
|
|
transport_->Close();
|
|
}
|
|
|
|
std::string FastbootDevice::GetCurrentSlot() {
|
|
// Check if a set_active ccommand was issued earlier since the boot control HAL
|
|
// returns the slot that is currently booted into.
|
|
if (!active_slot_.empty()) {
|
|
return active_slot_;
|
|
}
|
|
// Non-A/B devices must not have boot control HALs.
|
|
if (!boot_control_hal_) {
|
|
return "";
|
|
}
|
|
std::string suffix;
|
|
auto cb = [&suffix](hidl_string s) { suffix = s; };
|
|
boot_control_hal_->getSuffix(boot_control_hal_->getCurrentSlot(), cb);
|
|
return suffix;
|
|
}
|
|
|
|
bool FastbootDevice::WriteStatus(FastbootResult result, const std::string& message) {
|
|
constexpr size_t kResponseReasonSize = 4;
|
|
constexpr size_t kNumResponseTypes = 4; // "FAIL", "OKAY", "INFO", "DATA"
|
|
|
|
char buf[FB_RESPONSE_SZ];
|
|
constexpr size_t kMaxMessageSize = sizeof(buf) - kResponseReasonSize;
|
|
size_t msg_len = std::min(kMaxMessageSize, message.size());
|
|
|
|
constexpr const char* kResultStrings[kNumResponseTypes] = {RESPONSE_OKAY, RESPONSE_FAIL,
|
|
RESPONSE_INFO, RESPONSE_DATA};
|
|
|
|
if (static_cast<size_t>(result) >= kNumResponseTypes) {
|
|
return false;
|
|
}
|
|
|
|
memcpy(buf, kResultStrings[static_cast<size_t>(result)], kResponseReasonSize);
|
|
memcpy(buf + kResponseReasonSize, message.c_str(), msg_len);
|
|
|
|
size_t response_len = kResponseReasonSize + msg_len;
|
|
auto write_ret = this->get_transport()->Write(buf, response_len);
|
|
if (write_ret != static_cast<ssize_t>(response_len)) {
|
|
PLOG(ERROR) << "Failed to write " << message;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FastbootDevice::HandleData(bool read, std::vector<char>* data) {
|
|
return HandleData(read, data->data(), data->size());
|
|
}
|
|
|
|
bool FastbootDevice::HandleData(bool read, char* data, uint64_t size) {
|
|
auto read_write_data_size = read ? this->get_transport()->Read(data, size)
|
|
: this->get_transport()->Write(data, size);
|
|
if (read_write_data_size == -1) {
|
|
LOG(ERROR) << (read ? "read from" : "write to") << " transport failed";
|
|
return false;
|
|
}
|
|
if (static_cast<size_t>(read_write_data_size) != size) {
|
|
LOG(ERROR) << (read ? "read" : "write") << " expected " << size << " bytes, got "
|
|
<< read_write_data_size;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FastbootDevice::ExecuteCommands() {
|
|
char command[FB_RESPONSE_SZ + 1];
|
|
for (;;) {
|
|
auto bytes_read = transport_->Read(command, FB_RESPONSE_SZ);
|
|
if (bytes_read == -1) {
|
|
PLOG(ERROR) << "Couldn't read command";
|
|
return;
|
|
}
|
|
if (std::count_if(command, command + bytes_read, iscntrl) != 0) {
|
|
WriteStatus(FastbootResult::FAIL,
|
|
"Command contains control character");
|
|
continue;
|
|
}
|
|
command[bytes_read] = '\0';
|
|
|
|
LOG(INFO) << "Fastboot command: " << command;
|
|
|
|
std::vector<std::string> args;
|
|
std::string cmd_name;
|
|
if (android::base::StartsWith(command, "oem ")) {
|
|
args = {command};
|
|
cmd_name = FB_CMD_OEM;
|
|
} else {
|
|
args = android::base::Split(command, ":");
|
|
cmd_name = args[0];
|
|
}
|
|
|
|
auto found_command = kCommandMap.find(cmd_name);
|
|
if (found_command == kCommandMap.end()) {
|
|
WriteStatus(FastbootResult::FAIL, "Unrecognized command " + args[0]);
|
|
continue;
|
|
}
|
|
if (!found_command->second(this, args)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FastbootDevice::WriteOkay(const std::string& message) {
|
|
return WriteStatus(FastbootResult::OKAY, message);
|
|
}
|
|
|
|
bool FastbootDevice::WriteFail(const std::string& message) {
|
|
return WriteStatus(FastbootResult::FAIL, message);
|
|
}
|
|
|
|
bool FastbootDevice::WriteInfo(const std::string& message) {
|
|
return WriteStatus(FastbootResult::INFO, message);
|
|
}
|