/*
* Copyright 2024 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 "common/time_util.h"
#include "hal/snoop_logger.h"
#include "osi/include/allocator.h"
#include "stack/include/port_api.h"
#include "stack/include/rfcdefs.h"
#include "stack/test/common/stack_test_packet_utils.h"
#include "test/fake/fake_osi.h"
#include "test/mock/mock_btif_config.h"
#include "test/mock/mock_main_shim_entry.h"
#include "test/mock/mock_stack_acl.h"
#include "test/mock/mock_stack_btm_dev.h"
#include "test/mock/mock_stack_l2cap_api.h"
#include "test/mock/mock_stack_l2cap_ble.h"
#include "test/mock/mock_stack_l2cap_interface.h"
#include "test/rfcomm/stack_rfcomm_test_utils.h"
// TODO(b/369381361) Enfore -Wmissing-prototypes
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
#pragma GCC diagnostic ignored "-Wunused-parameter"
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::Unused;
namespace bluetooth {
namespace hal {
class SnoopLogger;
void SnoopLogger::AcceptlistRfcommDlci(uint16_t, uint16_t, uint8_t) {}
void SnoopLogger::SetRfcommPortOpen(uint16_t, uint16_t, uint8_t, uint16_t, bool) {}
void SnoopLogger::SetRfcommPortClose(uint16_t, uint16_t, uint8_t, uint16_t) {}
} // namespace hal
namespace common {
uint64_t time_get_os_boottime_ms() { return 0; }
} // namespace common
} // namespace bluetooth
namespace {
tL2CAP_APPL_INFO appl_info;
bluetooth::rfcomm::MockRfcommCallback* rfcomm_callback = nullptr;
tBTM_SEC_CALLBACK* security_callback = nullptr;
constexpr uint8_t kDummyId = 0x77;
constexpr uint8_t kDummyRemoteAddr[] = {0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC};
constexpr uint16_t kDummyCID = 0x1234;
constexpr uint8_t kDummyAddr[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
void port_mgmt_cback(const tPORT_RESULT code, uint16_t port_handle) {
rfcomm_callback->PortManagementCallback(code, port_handle, 0);
}
void port_event_cback(uint32_t code, uint16_t port_handle) {
rfcomm_callback->PortEventCallback(code, port_handle, 0);
}
class FakeBtStack {
NiceMock mock_l2cap_interface;
public:
NiceMock mock_rfcomm_callback;
FakeBtStack() {
ON_CALL(mock_l2cap_interface, L2CA_DataWrite)
.WillByDefault([](Unused, BT_HDR* hdr) {
osi_free(hdr);
return tL2CAP_DW_RESULT::SUCCESS;
});
ON_CALL(mock_l2cap_interface, L2CA_ConnectReq)
.WillByDefault([](Unused, Unused) { return kDummyCID; });
ON_CALL(mock_l2cap_interface, L2CA_DisconnectReq)
.WillByDefault([](Unused) { return true; });
ON_CALL(mock_l2cap_interface, L2CA_Register)
.WillByDefault([](uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info, Unused, Unused,
Unused, Unused, Unused) {
appl_info = p_cb_info;
return psm;
});
bluetooth::testing::stack::l2cap::set_interface(&mock_l2cap_interface);
rfcomm_callback = &mock_rfcomm_callback;
}
~FakeBtStack() {
rfcomm_callback = nullptr;
bluetooth::testing::stack::l2cap::reset_interface();
}
};
class Fakes {
public:
test::fake::FakeOsi fake_osi;
FakeBtStack fake_stack;
};
} // namespace
static int Cleanup(uint16_t* server_handle) { return RFCOMM_RemoveServer(*server_handle); }
static int ServerInit(FuzzedDataProvider* fdp, uint16_t* server_handle) {
RFCOMM_Init();
auto mtu = fdp->ConsumeIntegral();
auto scn = fdp->ConsumeIntegral();
auto uuid = fdp->ConsumeIntegral();
int status = RFCOMM_CreateConnectionWithSecurity(uuid, scn, true, mtu, kDummyAddr, server_handle,
port_mgmt_cback, 0);
if (status != PORT_SUCCESS) {
return status;
}
status = PORT_SetEventMaskAndCallback(*server_handle, PORT_EV_RXCHAR, port_event_cback);
return status;
}
static void FuzzAsServer(FuzzedDataProvider* fdp) {
auto server_handle = fdp->ConsumeIntegralInRange(1, MAX_RFC_PORTS);
if (ServerInit(fdp, &server_handle) != PORT_SUCCESS) {
return;
}
appl_info.pL2CA_ConnectInd_Cb(kDummyRemoteAddr, kDummyCID, 0, kDummyId);
// Simulating configuration confirmation event
tL2CAP_CFG_INFO cfg = {};
appl_info.pL2CA_ConfigCfm_Cb(kDummyCID, 0, &cfg);
// Feeding input packets
constexpr uint16_t kMaxPacketSize = 1024;
while (fdp->remaining_bytes() > 0) {
auto size = fdp->ConsumeIntegralInRange(0, kMaxPacketSize);
auto bytes = fdp->ConsumeBytes(size);
BT_HDR* hdr = reinterpret_cast(osi_calloc(sizeof(BT_HDR) + bytes.size()));
hdr->len = bytes.size();
std::copy(bytes.cbegin(), bytes.cend(), hdr->data);
appl_info.pL2CA_DataInd_Cb(kDummyCID, hdr);
}
// Simulating disconnecting event
appl_info.pL2CA_DisconnectInd_Cb(kDummyCID, false);
Cleanup(&server_handle);
}
static int ClientInit(FuzzedDataProvider* fdp, uint16_t* client_handle) {
RFCOMM_Init();
auto mtu = fdp->ConsumeIntegral();
auto scn = fdp->ConsumeIntegral();
auto uuid = fdp->ConsumeIntegral();
int status = RFCOMM_CreateConnectionWithSecurity(uuid, scn, false, mtu, kDummyAddr, client_handle,
port_mgmt_cback, 0);
if (status != PORT_SUCCESS) {
return status;
}
status = PORT_SetEventMaskAndCallback(*client_handle, PORT_EV_RXCHAR, port_event_cback);
return status;
}
static void FuzzAsClient(FuzzedDataProvider* fdp) {
auto client_handle = fdp->ConsumeIntegralInRange(1, MAX_RFC_PORTS);
if (ClientInit(fdp, &client_handle) != PORT_SUCCESS) {
return;
}
// Simulating outbound connection confirm event
appl_info.pL2CA_ConnectCfm_Cb(kDummyCID, tL2CAP_CONN::L2CAP_CONN_OK);
// Simulating configuration confirmation event
tL2CAP_CFG_INFO cfg = {};
appl_info.pL2CA_ConfigCfm_Cb(kDummyCID, 0, &cfg);
// Feeding input packets
constexpr uint16_t kMaxPacketSize = 1024;
while (fdp->remaining_bytes() > 0) {
auto size = fdp->ConsumeIntegralInRange(0, kMaxPacketSize);
auto bytes = fdp->ConsumeBytes(size);
BT_HDR* hdr = reinterpret_cast(osi_calloc(sizeof(BT_HDR) + bytes.size()));
hdr->len = bytes.size();
std::copy(bytes.cbegin(), bytes.cend(), hdr->data);
appl_info.pL2CA_DataInd_Cb(kDummyCID, hdr);
}
Cleanup(&client_handle);
}
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;
}