/* * Copyright 2022 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 "osi/include/allocator.h" #include "stack/include/bt_hdr.h" #include "stack/include/l2cdefs.h" #include "stack/include/sdpdefs.h" #include "stack/sdp/internal/sdp_api.h" #include "stack/sdp/sdpint.h" #include "test/fake/fake_osi.h" #include "test/mock/mock_btif_config.h" #include "test/mock/mock_stack_l2cap_api.h" #include "types/bluetooth/uuid.h" // TODO(b/369381361) Enfore -Wmissing-prototypes #pragma GCC diagnostic ignored "-Wmissing-prototypes" #pragma GCC diagnostic ignored "-Wunused-parameter" namespace { #define SDP_DB_SIZE 0x10000 constexpr uint16_t kDummyCID = 0x1234; constexpr uint16_t kDummyPSM = 0x7788; constexpr uint8_t kDummyID = 0x99; constexpr uint8_t kDummyAddr[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; // Set up default callback structure tL2CAP_APPL_INFO cb_info = { .pL2CA_ConnectInd_Cb = [](const RawAddress& bd_addr, uint16_t lcid, uint16_t psm, uint8_t id) {}, // tL2CA_CONNECT_IND_CB .pL2CA_ConnectCfm_Cb = [](uint16_t lcid, tL2CAP_CONN result) {}, // tL2CA_CONNECT_CFM_CB .pL2CA_ConfigInd_Cb = [](uint16_t lcid, tL2CAP_CFG_INFO* p_cfg) {}, // tL2CA_CONFIG_IND_CB .pL2CA_ConfigCfm_Cb = [](uint16_t lcid, uint16_t initiator, tL2CAP_CFG_INFO* p_cfg) {}, // tL2CA_CONFIG_CFM_CB .pL2CA_DisconnectInd_Cb = [](uint16_t lcid, bool should_ack) {}, // tL2CA_DISCONNECT_IND_CB .pL2CA_DisconnectCfm_Cb = [](uint16_t lcid, uint16_t result) {}, // tL2CA_DISCONNECT_CFM_CB .pL2CA_DataInd_Cb = [](uint16_t lcid, BT_HDR* data) {}, // tL2CA_DATA_IND_CB .pL2CA_CongestionStatus_Cb = [](uint16_t lcid, bool is_congested) {}, // tL2CA_CONGESTION_STATUS_CB .pL2CA_TxComplete_Cb = [](uint16_t lcid, uint16_t num_sdu) {}, // tL2CA_TX_COMPLETE_CB .pL2CA_Error_Cb = [](uint16_t lcid, uint16_t error_type) {}, // tL2CA_ERROR_CB .pL2CA_CreditBasedConnectInd_Cb = [](const RawAddress& bdaddr, std::vector& lcids, uint16_t psm, uint16_t peer_mtu, uint8_t identifier) {}, // tL2CA_CREDIT_BASED_CONNECT_IND_CB .pL2CA_CreditBasedConnectCfm_Cb = [](const RawAddress& bdaddr, uint16_t lcid, uint16_t peer_mtu, tL2CAP_LE_RESULT_CODE result) {}, // tL2CA_CREDIT_BASED_CONNECT_CFM_CB .pL2CA_CreditBasedReconfigCompleted_Cb = [](const RawAddress& bdaddr, uint16_t lcid, bool is_local_cfg, tL2CAP_LE_CFG_INFO* p_cfg) {}, // tL2CA_CREDIT_BASED_RECONFIG_COMPLETED_CB .pL2CA_CreditBasedCollisionInd_Cb = [](const RawAddress& bdaddr) {}, // tL2CA_CREDIT_BASED_COLLISION_IND_CB }; class FakeL2cap { public: FakeL2cap() { test::mock::stack_l2cap_api::L2CA_ConnectReq.body = [](uint16_t psm, const RawAddress& raw_address) { return kDummyCID; }; test::mock::stack_l2cap_api::L2CA_ConnectReqWithSecurity.body = [](uint16_t psm, const RawAddress& p_bd_addr, uint16_t sec_level) { return bluetooth::stack::l2cap::get_interface().L2CA_ConnectReq(psm, p_bd_addr); }; test::mock::stack_l2cap_api::L2CA_DataWrite.body = [](uint16_t cid, BT_HDR* p_data) -> tL2CAP_DW_RESULT { auto len = p_data->len; osi_free(p_data); return tL2CAP_DW_RESULT::SUCCESS; }; test::mock::stack_l2cap_api::L2CA_DisconnectReq.body = [](uint16_t lcid) { return true; }; test::mock::stack_l2cap_api::L2CA_RegisterWithSecurity.body = [](uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info, bool enable_snoop, tL2CAP_ERTM_INFO* p_ertm_info, uint16_t my_mtu, uint16_t required_remote_mtu, uint16_t sec_level) { cb_info = p_cb_info; return psm; }; } ~FakeL2cap() { test::mock::stack_l2cap_api::L2CA_ConnectReq = {}; test::mock::stack_l2cap_api::L2CA_ConnectReqWithSecurity = {}; test::mock::stack_l2cap_api::L2CA_DataWrite = {}; test::mock::stack_l2cap_api::L2CA_DisconnectReq = {}; test::mock::stack_l2cap_api::L2CA_RegisterWithSecurity = {}; } }; class FakeBtifConfig { public: FakeBtifConfig() { test::mock::btif_config::btif_config_set_bin.body = [](const std::string&, const std::string&, const uint8_t*, size_t) { // This function is not properly mocked. The abort here allows us to // catch any cases using this mock. abort(); return true; }; test::mock::btif_config::btif_config_set_int.body = [](const std::string& section, const std::string& key, int value) { // This function is not properly mocked. The abort here allows us to // catch any cases using this mock. abort(); return true; }; } ~FakeBtifConfig() { test::mock::btif_config::btif_config_set_bin = {}; test::mock::btif_config::btif_config_set_int = {}; } }; class Fakes { public: test::fake::FakeOsi fake_osi; FakeL2cap fake_l2cap; FakeBtifConfig fake_btif_config; }; } // namespace static void FuzzAsServer(FuzzedDataProvider& fdp) { std::vector> attrs; sdp_init(); auto rec_num = fdp.ConsumeIntegralInRange(0, 10); for (uint8_t i = 0; i < rec_num; i++) { auto handle = SDP_CreateRecord(); auto attr_num = fdp.ConsumeIntegralInRange(0, 10); for (uint8_t s = 0; s < attr_num; s++) { auto id = (i == 0) ? ATTR_ID_BT_PROFILE_DESC_LIST : fdp.ConsumeIntegral(); auto type = fdp.ConsumeIntegral(); auto len = fdp.ConsumeIntegralInRange(1, 512); auto data = fdp.ConsumeBytes(len); if (data.size() == 0) { break; } attrs.push_back(data); SDP_AddAttribute(handle, id, type, data.size(), data.data()); } } cb_info.pL2CA_ConnectInd_Cb(RawAddress(kDummyAddr), kDummyCID, kDummyPSM, kDummyID); tL2CAP_CFG_INFO cfg = {}; cb_info.pL2CA_ConfigCfm_Cb(kDummyCID, 0, &cfg); while (fdp.remaining_bytes() > 0) { auto size = fdp.ConsumeIntegralInRange(0, 1024); auto bytes = fdp.ConsumeBytes(size); BT_HDR* hdr = (BT_HDR*)osi_calloc(sizeof(BT_HDR) + bytes.size()); hdr->len = bytes.size(); std::copy(bytes.cbegin(), bytes.cend(), hdr->data); cb_info.pL2CA_DataInd_Cb(kDummyCID, hdr); } cb_info.pL2CA_DisconnectInd_Cb(kDummyCID, false); sdp_free(); } static void FuzzAsClient(FuzzedDataProvider& fdp) { std::shared_ptr p_db((tSDP_DISCOVERY_DB*)malloc(SDP_DB_SIZE), free); std::vector init_uuids; std::vector init_attrs; sdp_init(); uint8_t num_uuid = fdp.ConsumeIntegralInRange(0, SDP_MAX_UUID_FILTERS); uint8_t num_attr = fdp.ConsumeIntegralInRange(0, SDP_MAX_ATTR_FILTERS); for (uint8_t i = 0; i < num_uuid; i++) { init_uuids.push_back(bluetooth::Uuid::From16Bit(fdp.ConsumeIntegral())); } for (uint8_t i = 0; i < num_attr; i++) { init_attrs.push_back(fdp.ConsumeIntegral()); } SDP_InitDiscoveryDb(p_db.get(), SDP_DB_SIZE, init_uuids.size(), init_uuids.data(), init_attrs.size(), init_attrs.data()); bool is_di_discover = fdp.ConsumeBool(); if (is_di_discover) { SDP_ServiceSearchRequest(kDummyAddr, p_db.get(), [](const RawAddress& bd_addr, tSDP_RESULT result) {}); } else { SDP_ServiceSearchAttributeRequest(kDummyAddr, p_db.get(), [](const RawAddress& bd_addr, tSDP_RESULT result) {}); } cb_info.pL2CA_ConnectCfm_Cb(kDummyCID, tL2CAP_CONN::L2CAP_CONN_OK); tL2CAP_CFG_INFO cfg = {}; cb_info.pL2CA_ConfigCfm_Cb(kDummyCID, 0, &cfg); while (fdp.remaining_bytes() > 0) { auto size = fdp.ConsumeIntegralInRange(0, 1024); auto bytes = fdp.ConsumeBytes(size); BT_HDR* hdr = (BT_HDR*)osi_calloc(sizeof(BT_HDR) + bytes.size()); hdr->len = bytes.size(); std::copy(bytes.cbegin(), bytes.cend(), hdr->data); cb_info.pL2CA_DataInd_Cb(kDummyCID, hdr); } cb_info.pL2CA_DisconnectInd_Cb(kDummyCID, false); sdp_free(); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { auto fakes = std::make_unique(); FuzzedDataProvider fdp(data, size); if (fdp.ConsumeBool()) { FuzzAsServer(fdp); } else { FuzzAsClient(fdp); } return 0; }