Added bluetoothV1.0_fuzzer
Test: ./bluetoothV1.0_fuzzer
Bug: 187131546
Change-Id: If73b3e9fa799057ae9ca8a1eba0e6a02c66498c0
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 0000000..4ebeb28
--- /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 0000000..edd4fb6
--- /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 0000000..90cdc66
--- /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 <android/hardware/bluetooth/1.0/IBluetoothHci.h>
+#include <android/hardware/bluetooth/1.0/IBluetoothHciCallbacks.h>
+#include <bluetooth_address.h>
+#include <bluetooth_hci.h>
+#include <cutils/properties.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <log/log.h>
+
+#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 <typename T>
+const hidl_vec<T> toHidlVec(const std::vector<T>& vec) {
+ hidl_vec<T> hVec;
+ hVec.setToExternal(const_cast<T*>(vec.data()), vec.size());
+ return hVec;
+}
+
+class BluetoothHciCallbacks : public IBluetoothHciCallbacks {
+ public:
+ virtual ~BluetoothHciCallbacks() = default;
+
+ Return<void> initializationComplete(Status status) override {
+ if (status == Status::SUCCESS) {
+ isInitialized = true;
+ } else {
+ isInitialized = false;
+ }
+ return Return<void>();
+ };
+
+ Return<void> hciEventReceived(
+ const ::android::hardware::hidl_vec<uint8_t>& /*event*/) override {
+ return Return<void>();
+ };
+
+ Return<void> aclDataReceived(
+ const ::android::hardware::hidl_vec<uint8_t>& /*data*/) override {
+ return Return<void>();
+ };
+
+ Return<void> scoDataReceived(
+ const ::android::hardware::hidl_vec<uint8_t>& /*data*/) override {
+ return Return<void>();
+ };
+ 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<BluetoothHci> mBtHci = nullptr;
+ FuzzedDataProvider* mFdp = nullptr;
+};
+
+bool BluetoothFuzzer::init(const uint8_t* data, size_t size) {
+ mBtHci = sp<BluetoothHci>::make();
+ if (!mBtHci) {
+ return false;
+ }
+ mFdp = new FuzzedDataProvider(data, size);
+ return true;
+}
+
+void BluetoothFuzzer::process() {
+ sp<BluetoothHciCallbacks> bluetoothCallback =
+ sp<BluetoothHciCallbacks>::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<size_t>(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<uint8_t> hciPacket, aclPacket;
+
+ size_t hciPacketSize =
+ mFdp->ConsumeIntegralInRange<size_t>(0, kMaxPacketSize);
+ hciPacket = mFdp->ConsumeBytes<uint8_t>(hciPacketSize);
+ mBtHci->sendHciCommand(toHidlVec(hciPacket));
+
+ size_t aclPacketSize =
+ mFdp->ConsumeIntegralInRange<size_t>(0, kMaxPacketSize);
+ aclPacket = mFdp->ConsumeBytes<uint8_t>(aclPacketSize);
+ mBtHci->sendAclData(toHidlVec(aclPacket));
+
+ if (shouldSetH4Protocol) {
+ std::vector<uint8_t> scoPacket;
+ size_t scoPacketSize =
+ mFdp->ConsumeIntegralInRange<size_t>(0, kMaxPacketSize);
+ scoPacket = mFdp->ConsumeBytes<uint8_t>(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 0000000..897fb67
--- /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 0000000..ca227ea
--- /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__