From a37d3df9f842e6aae13637f06e59fde75db2ad41 Mon Sep 17 00:00:00 2001 From: Ayushi Khopkar Date: Thu, 24 Jun 2021 20:24:03 +0530 Subject: [PATCH 1/2] Added cc_defaults for android.hardware.bluetooth@1.0-impl Created a new library - android.hardware.bluetooth@1.0-impl-test, that shall be used for testing by bluetoothV1.0_fuzzer Test: Build android.hardware.bluetooth@1.0-impl Test: Build android.hardware.bluetooth@1.0-impl-test Bug: 187131546 Change-Id: If67bf4ff59fc446dfa0548ecb8f67359717f6cc9 --- bluetooth/1.0/default/Android.bp | 24 ++++++++++++++++++++--- bluetooth/1.0/default/vendor_interface.cc | 4 ++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/bluetooth/1.0/default/Android.bp b/bluetooth/1.0/default/Android.bp index ee368fd6eb..84a49cf0a7 100644 --- a/bluetooth/1.0/default/Android.bp +++ b/bluetooth/1.0/default/Android.bp @@ -22,9 +22,8 @@ package { default_applicable_licenses: ["hardware_interfaces_license"], } -cc_library { - name: "android.hardware.bluetooth@1.0-impl", - defaults: ["hidl_defaults"], +cc_defaults { + name: "android.hardware.bluetooth@1.0-defaults", vendor: true, relative_install_path: "hw", srcs: [ @@ -47,6 +46,25 @@ cc_library { ], } +cc_library { + name: "android.hardware.bluetooth@1.0-impl", + defaults: [ + "hidl_defaults", + "android.hardware.bluetooth@1.0-defaults", + ], +} + +cc_library { + name: "android.hardware.bluetooth@1.0-impl-test", + defaults: [ + "hidl_defaults", + "android.hardware.bluetooth@1.0-defaults", + ], + cflags: [ + "-DBT_FUZZER", + ], +} + cc_library_static { name: "android.hardware.bluetooth-async", vendor: true, diff --git a/bluetooth/1.0/default/vendor_interface.cc b/bluetooth/1.0/default/vendor_interface.cc index d809313db6..1d15dd6f11 100644 --- a/bluetooth/1.0/default/vendor_interface.cc +++ b/bluetooth/1.0/default/vendor_interface.cc @@ -27,7 +27,11 @@ #include "h4_protocol.h" #include "mct_protocol.h" +#ifdef BT_FUZZER +static const char* VENDOR_LIBRARY_NAME = "libbt-vendor-fuzz.so"; +#else static const char* VENDOR_LIBRARY_NAME = "libbt-vendor.so"; +#endif static const char* VENDOR_LIBRARY_SYMBOL_NAME = "BLUETOOTH_VENDOR_LIB_INTERFACE"; From 7c0bcf3814f07254456e2d09be90e08f6b09407e Mon Sep 17 00:00:00 2001 From: Ayushi Khopkar Date: Tue, 1 Jun 2021 10:34:35 +0530 Subject: [PATCH 2/2] Added bluetoothV1.0_fuzzer Test: ./bluetoothV1.0_fuzzer Bug: 187131546 Change-Id: If73b3e9fa799057ae9ca8a1eba0e6a02c66498c0 --- bluetooth/1.0/default/test/fuzzer/Android.bp | 55 +++++ bluetooth/1.0/default/test/fuzzer/README.md | 48 +++++ .../test/fuzzer/bluetoothV1.0_fuzzer.cpp | 198 ++++++++++++++++++ .../1.0/default/test/fuzzer/bt_vendor.cpp | 162 ++++++++++++++ bluetooth/1.0/default/test/fuzzer/bt_vendor.h | 60 ++++++ 5 files changed, 523 insertions(+) create mode 100644 bluetooth/1.0/default/test/fuzzer/Android.bp create mode 100644 bluetooth/1.0/default/test/fuzzer/README.md create mode 100644 bluetooth/1.0/default/test/fuzzer/bluetoothV1.0_fuzzer.cpp create mode 100644 bluetooth/1.0/default/test/fuzzer/bt_vendor.cpp create mode 100644 bluetooth/1.0/default/test/fuzzer/bt_vendor.h diff --git a/bluetooth/1.0/default/test/fuzzer/Android.bp b/bluetooth/1.0/default/test/fuzzer/Android.bp new file mode 100644 index 0000000000..4ebeb28785 --- /dev/null +++ b/bluetooth/1.0/default/test/fuzzer/Android.bp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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. + * + */ +cc_library { + name: "libbt-vendor-fuzz", + vendor: true, + srcs: [ + "bt_vendor.cpp", + ], + static_libs: [ + "android.hardware.bluetooth@1.0-impl-test", + "android.hardware.bluetooth-hci", + ], +} + +cc_fuzz { + name: "bluetoothV1.0_fuzzer", + vendor: true, + srcs: [ + "bluetoothV1.0_fuzzer.cpp", + ], + static_libs: [ + "android.hardware.bluetooth@1.0-impl-test", + "android.hardware.bluetooth-async", + "android.hardware.bluetooth-hci", + "libcutils", + "libutils", + ], + shared_libs: [ + "android.hardware.bluetooth@1.0", + "libhardware", + "libhidlbase", + "libbt-vendor-fuzz", + "liblog", + ], + fuzz_config: { + cc: [ + "android-media-fuzzing-reports@google.com", + ], + componentid: 533764, + }, +} diff --git a/bluetooth/1.0/default/test/fuzzer/README.md b/bluetooth/1.0/default/test/fuzzer/README.md new file mode 100644 index 0000000000..edd4fb655e --- /dev/null +++ b/bluetooth/1.0/default/test/fuzzer/README.md @@ -0,0 +1,48 @@ +# Fuzzer for android.hardware.bluetooth@1.0-impl-test + +## Plugin Design Considerations +The fuzzer plugin for android.hardware.bluetooth@1.0-impl-test is designed based on the understanding of the source code and tries to achieve the following: + +##### Maximize code coverage +1. The configuration parameters are not hardcoded, but instead selected based on +incoming data. This ensures more code paths are reached by the fuzzer. + +2. A new library *'libbt-vendor-fuzz.so'* is created that implements functions of `bt_vendor_interface_t` and calls them in order to maximize the code coverage + +android.hardware.bluetooth@1.0-impl-test supports the following parameters: + +1. Bluetooth Address (parameter name: `btAddress`) + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +| `btAddress` | Values inside array ranges from `0x0` to `0xFF`| Value obtained from FuzzedDataProvider| + +This also ensures that the plugin is always deterministic for any given input. + +##### Maximize utilization of input data +The plugin feeds the entire input data to the module. +This ensures that the plugin tolerates any kind of input (empty, huge, +malformed, etc) and doesnt `exit()` on any input and thereby increasing the +chance of identifying vulnerabilities. + +## Build + +This describes steps to build bluetoothV1.0_fuzzer binary. + +### Android + +#### Steps to build +Build the fuzzer +``` + $ mm -j$(nproc) bluetoothV1.0_fuzzer +``` +#### Steps to run +To run on device +``` + $ adb sync data + $ adb shell LD_LIBRARY_PATH=/data/fuzz/${TARGET_ARCH}/lib/ /data/fuzz/${TARGET_ARCH}/bluetoothV1.0_fuzzer/bluetoothV1.0_fuzzer +``` + +## References: + * http://llvm.org/docs/LibFuzzer.html + * https://github.com/google/oss-fuzz diff --git a/bluetooth/1.0/default/test/fuzzer/bluetoothV1.0_fuzzer.cpp b/bluetooth/1.0/default/test/fuzzer/bluetoothV1.0_fuzzer.cpp new file mode 100644 index 0000000000..90cdc66bdc --- /dev/null +++ b/bluetooth/1.0/default/test/fuzzer/bluetoothV1.0_fuzzer.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2021 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 +#include +#include +#include +#include +#include +#include + +#include "bt_vendor.h" + +using namespace std; +using ::android::sp; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::bluetooth::V1_0::IBluetoothHci; +using ::android::hardware::bluetooth::V1_0::IBluetoothHciCallbacks; +using ::android::hardware::bluetooth::V1_0::Status; +using ::android::hardware::bluetooth::V1_0::implementation::BluetoothAddress; +using ::android::hardware::bluetooth::V1_0::implementation::BluetoothHci; +using ::android::hardware::bluetooth::V1_0::implementation:: + FACTORY_BDADDR_PROPERTY; +using ::android::hardware::bluetooth::V1_0::implementation:: + PERSIST_BDADDR_PROPERTY; +using ::android::hardware::bluetooth::V1_0::implementation:: + PROPERTY_BT_BDADDR_PATH; + +constexpr size_t kMaxPacketSize = 100; +constexpr size_t kMinFdcount = 2; + +template +const hidl_vec toHidlVec(const std::vector& vec) { + hidl_vec hVec; + hVec.setToExternal(const_cast(vec.data()), vec.size()); + return hVec; +} + +class BluetoothHciCallbacks : public IBluetoothHciCallbacks { + public: + virtual ~BluetoothHciCallbacks() = default; + + Return initializationComplete(Status status) override { + if (status == Status::SUCCESS) { + isInitialized = true; + } else { + isInitialized = false; + } + return Return(); + }; + + Return hciEventReceived( + const ::android::hardware::hidl_vec& /*event*/) override { + return Return(); + }; + + Return aclDataReceived( + const ::android::hardware::hidl_vec& /*data*/) override { + return Return(); + }; + + Return scoDataReceived( + const ::android::hardware::hidl_vec& /*data*/) override { + return Return(); + }; + bool isInitialized; +}; + +class BluetoothFuzzer { + public: + ~BluetoothFuzzer() { + if (mFdp) { + delete mFdp; + } + mBtHci->close(); + mBtHci.clear(); + } + bool init(const uint8_t* data, size_t size); + void process(); + + private: + sp mBtHci = nullptr; + FuzzedDataProvider* mFdp = nullptr; +}; + +bool BluetoothFuzzer::init(const uint8_t* data, size_t size) { + mBtHci = sp::make(); + if (!mBtHci) { + return false; + } + mFdp = new FuzzedDataProvider(data, size); + return true; +} + +void BluetoothFuzzer::process() { + sp bluetoothCallback = + sp::make(); + + uint8_t btAddress[BluetoothAddress::kBytes]; + mFdp->ConsumeData(btAddress, sizeof(uint8_t) * BluetoothAddress::kBytes); + + char btAddrString[BluetoothAddress::kStringLength + 1]; + BluetoothAddress::bytes_to_string(btAddress, btAddrString); + + /* property_set() is called so that BluetoothAddress::get_local_address() + * could return true and the LOG_ALWAYS_FATAL() that aborts the run, if + * BluetoothAddress::get_local_address() returns false, could be avoided. + * + * BluetoothAddress::get_local_address() first searches if + * PROPERTY_BT_BDADDR_PATH is set, if it fails to get PROPERTY_BT_BDADDR_PATH, + * it searches for FACTORY_BDADDR_PROPERTY. If it fails to get + * FACTORY_BDADDR_PROPERTY, it then searches for PERSIST_BDADDR_PROPERTY. If + * PERSIST_BDADDR_PROPERTY is also not set, it results in an abort. + */ + property_set(PERSIST_BDADDR_PROPERTY, btAddrString); + + if (mFdp->ConsumeBool()) { + property_set(FACTORY_BDADDR_PROPERTY, btAddrString); + } + + if (mFdp->ConsumeBool()) { + char property[PROPERTY_VALUE_MAX] = {0}; + property_get("ro.vendor.bt.bdaddr_path", property, NULL); + // get the value of ro.vendor.bt.bdaddr_path and set it to + // PROPERTY_BT_BDADDR_PATH + property_set(PROPERTY_BT_BDADDR_PATH, property); + } + + bool shouldSetH4Protocol = mFdp->ConsumeBool(); + BtVendor* btVendor = BtVendor::getInstance(); + + size_t fdcount = 1; + int32_t fdList[CH_MAX] = {0}; + if (!shouldSetH4Protocol) { + fdcount = mFdp->ConsumeIntegralInRange(kMinFdcount, CH_MAX - 1); + } + + for (size_t i = 0; i < fdcount; ++i) { + fdList[i] = open("/dev/null", O_RDWR | O_CREAT); + } + + btVendor->populateFdList(fdList, fdcount); + mBtHci->initialize(bluetoothCallback); + + if (!bluetoothCallback->isInitialized) { + return; + } + + std::vector hciPacket, aclPacket; + + size_t hciPacketSize = + mFdp->ConsumeIntegralInRange(0, kMaxPacketSize); + hciPacket = mFdp->ConsumeBytes(hciPacketSize); + mBtHci->sendHciCommand(toHidlVec(hciPacket)); + + size_t aclPacketSize = + mFdp->ConsumeIntegralInRange(0, kMaxPacketSize); + aclPacket = mFdp->ConsumeBytes(aclPacketSize); + mBtHci->sendAclData(toHidlVec(aclPacket)); + + if (shouldSetH4Protocol) { + std::vector scoPacket; + size_t scoPacketSize = + mFdp->ConsumeIntegralInRange(0, kMaxPacketSize); + scoPacket = mFdp->ConsumeBytes(scoPacketSize); + mBtHci->sendScoData(toHidlVec(scoPacket)); + } + + btVendor->callRemainingCbacks(); + + for (size_t i = 0; i < fdcount; ++i) { + if (fdList[i]) { + close(fdList[i]); + } + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + BluetoothFuzzer bluetoothFuzzer; + if (bluetoothFuzzer.init(data, size)) { + bluetoothFuzzer.process(); + } + return 0; +} diff --git a/bluetooth/1.0/default/test/fuzzer/bt_vendor.cpp b/bluetooth/1.0/default/test/fuzzer/bt_vendor.cpp new file mode 100644 index 0000000000..897fb67d98 --- /dev/null +++ b/bluetooth/1.0/default/test/fuzzer/bt_vendor.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2021 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 "bt_vendor.h" + +#define UNUSED_PARAM __attribute__((unused)) +#define HCI_CMD_PREAMBLE_SIZE 3 +#define HCI_RESET 0x0C03 +#define HCI_EVT_CMD_CMPL_OPCODE 3 +#define HCI_EVT_CMD_CMPL_STATUS_RET_BYTE 5 +#define MSG_STACK_TO_HC_HCI_CMD 0x2000 +#define BT_HC_HDR_SIZE (sizeof(HC_BT_HDR)) +#define STREAM_TO_UINT16(u16, p) \ + { \ + u16 = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); \ + (p) += 2; \ + } +#define UINT16_TO_STREAM(p, u16) \ + { \ + *(p)++ = (uint8_t)(u16); \ + *(p)++ = (uint8_t)((u16) >> 8); \ + } +bt_vendor_callbacks_t* bt_vendor_cbacks = nullptr; + +void hw_epilog_cback(void* p_mem) { + HC_BT_HDR* p_evt_buf = (HC_BT_HDR*)p_mem; + uint8_t *p, status; + uint16_t opcode; + + status = *((uint8_t*)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_STATUS_RET_BYTE); + p = (uint8_t*)(p_evt_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE; + STREAM_TO_UINT16(opcode, p); + + if (!bt_vendor_cbacks) { + return; + } + /* Must free the RX event buffer */ + bt_vendor_cbacks->dealloc(p_evt_buf); + + /* Once epilog process is done, must call callback to notify caller */ + bt_vendor_cbacks->epilog_cb(BT_VND_OP_RESULT_SUCCESS); + return; +} + +static int testInit(const bt_vendor_callbacks_t* cb, + unsigned char* bdaddr UNUSED_PARAM) { + if (cb == nullptr) { + return -1; + } + /*store reference to user callbacks */ + bt_vendor_cbacks = (bt_vendor_callbacks_t*)cb; + return 0; +} + +static int testOperations(bt_vendor_opcode_t opcode, void* param UNUSED_PARAM) { + BtVendor* btVendor = BtVendor::getInstance(); + if (bt_vendor_cbacks) { + btVendor->setVendorCback(bt_vendor_cbacks, opcode); + } + switch (opcode) { + case BT_VND_OP_POWER_CTRL: { + // No callback for this opcode + break; + } + case BT_VND_OP_USERIAL_OPEN: { + int32_t(*fd_array)[] = (int32_t(*)[])param; + int32_t fdArray[CH_MAX]; + *fdArray = *(btVendor->queryFdList()); + size_t fdcount = btVendor->queryFdCount(); + for (size_t i = 0; i < fdcount; ++i) { + (*fd_array)[i] = fdArray[i]; + } + return fdcount; + break; + } + case BT_VND_OP_FW_CFG: { + if (bt_vendor_cbacks) { + bt_vendor_cbacks->fwcfg_cb(BT_VND_OP_RESULT_SUCCESS); + } + break; + } + case BT_VND_OP_GET_LPM_IDLE_TIMEOUT: { + // No callback for this opcode + uint32_t* timeout_ms = (uint32_t*)param; + *timeout_ms = 0; + break; + } + case BT_VND_OP_LPM_SET_MODE: { + if (bt_vendor_cbacks) { + bt_vendor_cbacks->lpm_cb(BT_VND_OP_RESULT_SUCCESS); + } + break; + } + case BT_VND_OP_USERIAL_CLOSE: { + // No callback for this opcode + break; + } + case BT_VND_OP_LPM_WAKE_SET_STATE: { + // No callback for this opcode + break; + } + default: + break; + } + return 0; +} + +static void testCleanup(void) { bt_vendor_cbacks = nullptr; } + +const bt_vendor_interface_t BLUETOOTH_VENDOR_LIB_INTERFACE = { + sizeof(bt_vendor_interface_t), testInit, testOperations, testCleanup}; + +void BtVendor::populateFdList(int32_t list[], size_t count) { + fdCount = count; + for (size_t i = 0; i < count; ++i) { + fdList[i] = list[i]; + } +} + +void BtVendor::callRemainingCbacks() { + if (mCbacks) { + mCbacks->audio_state_cb(BT_VND_OP_RESULT_SUCCESS); + mCbacks->scocfg_cb(BT_VND_OP_RESULT_SUCCESS); + mCbacks->a2dp_offload_cb(BT_VND_OP_RESULT_SUCCESS, mOpcode, 0); + mCbacks->epilog_cb(BT_VND_OP_RESULT_SUCCESS); + + HC_BT_HDR* p_buf = NULL; + uint8_t* p; + + /* Sending a HCI_RESET */ + /* Must allocate command buffer via HC's alloc API */ + p_buf = (HC_BT_HDR*)mCbacks->alloc(BT_HC_HDR_SIZE + HCI_CMD_PREAMBLE_SIZE); + if (p_buf) { + p_buf->event = MSG_STACK_TO_HC_HCI_CMD; + p_buf->offset = 0; + p_buf->layer_specific = 0; + p_buf->len = HCI_CMD_PREAMBLE_SIZE; + + p = (uint8_t*)(p_buf + 1); + UINT16_TO_STREAM(p, HCI_RESET); + *p = 0; /* parameter length */ + + /* Send command via HC's xmit_cb API */ + mCbacks->xmit_cb(HCI_RESET, p_buf, hw_epilog_cback); + } else { + mCbacks->epilog_cb(BT_VND_OP_RESULT_FAIL); + } + } +} diff --git a/bluetooth/1.0/default/test/fuzzer/bt_vendor.h b/bluetooth/1.0/default/test/fuzzer/bt_vendor.h new file mode 100644 index 0000000000..ca227eafd1 --- /dev/null +++ b/bluetooth/1.0/default/test/fuzzer/bt_vendor.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 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. + * + */ +#ifndef __BT_VENDOR_H__ +#define __BT_VENDOR_H__ + +#include "bt_vendor_lib.h" + +class BtVendor { + public: + static BtVendor* getInstance() { + if (!mInstance) { + mInstance = new BtVendor; + } + return mInstance; + } + + void setVendorCback(bt_vendor_callbacks_t* cb, bt_vendor_opcode_t opcode) { + mCbacks = cb; + mOpcode = opcode; + } + + int32_t* queryFdList() { return fdList; } + size_t queryFdCount() { return fdCount; } + void callRemainingCbacks(); + void populateFdList(int32_t list[], size_t count); + + private: + BtVendor() = default; + + ~BtVendor() { + if (mInstance) { + delete mInstance; + mInstance = nullptr; + } + mCbacks = nullptr; + } + + static BtVendor* mInstance; + bt_vendor_callbacks_t* mCbacks = nullptr; + bt_vendor_opcode_t mOpcode; + int32_t fdCount; + int32_t fdList[CH_MAX] = {0}; +}; + +BtVendor* BtVendor::mInstance = nullptr; +#endif // __BT_VENDOR_H__