blob: d240857dfcd09a22c7a965810d121003e081c82f [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_TESTING_PEER_FUZZER_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_TESTING_PEER_FUZZER_H_
#include <functional>
#include <fuzzer/FuzzedDataProvider.h>
#include "src/connectivity/bluetooth/core/bt-host/gap/peer.h"
namespace bt {
namespace testing {
inline DeviceAddress MakePublicDeviceAddress(FuzzedDataProvider &fdp) {
DeviceAddressBytes device_address_bytes;
fdp.ConsumeData(&device_address_bytes, sizeof(device_address_bytes));
return DeviceAddress(
fdp.PickValueInArray({DeviceAddress::Type::kBREDR, DeviceAddress::Type::kLEPublic}),
device_address_bytes);
}
} // namespace testing
namespace gap::testing {
class PeerFuzzer final {
public:
// Core Spec v5.2, Vol 6, Part B, Section 2.3.4.9
static constexpr size_t kMaxLeAdvDataLength = 1650;
// Create a PeerFuzzer that mutates |peer| using |fuzzed_data_provider|. Both arguments must
// outlive this object.
PeerFuzzer(FuzzedDataProvider &fuzzed_data_provider, Peer &peer)
: fuzzed_data_provider_(fuzzed_data_provider), peer_(peer) {}
// Use the FuzzedDataProvider with which this object was constructed to choose a member function
// that is then used to mutate the corresponding Peer field.
void FuzzOneField() {
// The decltype is easier to read than void (&PeerFuzzer::*)(), but this function isn't special
// and should not pick itself
using FuzzMemberFunction = decltype(&PeerFuzzer::FuzzOneField);
constexpr FuzzMemberFunction kFuzzFunctions[] = {
&PeerFuzzer::LEDataSetAdvertisingData,
&PeerFuzzer::LEDataSetConnectionState,
&PeerFuzzer::LEDataSetConnectionParameters,
&PeerFuzzer::LEDataSetPreferredConnectionParameters,
&PeerFuzzer::LEDataSetBondData,
&PeerFuzzer::LEDataClearBondData,
&PeerFuzzer::LEDataSetFeatures,
&PeerFuzzer::LEDataSetServiceChangedGattData,
&PeerFuzzer::LEDataSetAutoConnectBehavior,
&PeerFuzzer::BrEdrDataSetInquiryData<hci::InquiryResult>,
&PeerFuzzer::BrEdrDataSetInquiryData<hci::InquiryResultRSSI>,
&PeerFuzzer::BrEdrDataSetInquiryData<hci::ExtendedInquiryResultEventParams>,
&PeerFuzzer::BrEdrDataSetConnectionState,
&PeerFuzzer::BrEdrDataSetBondData,
&PeerFuzzer::BrEdrDataClearBondData,
&PeerFuzzer::BrEdrDataAddService,
&PeerFuzzer::SetName,
&PeerFuzzer::SetFeaturePage,
&PeerFuzzer::set_last_page_number,
&PeerFuzzer::set_version,
&PeerFuzzer::set_identity_known,
&PeerFuzzer::StoreBrEdrCrossTransportKey,
&PeerFuzzer::set_connectable,
};
std::invoke(fdp().PickValueInArray(kFuzzFunctions), this);
}
void LEDataSetAdvertisingData() {
peer_.MutLe().SetAdvertisingData(
fdp().ConsumeIntegral<uint8_t>(),
DynamicByteBuffer(BufferView(fdp().ConsumeBytes<uint8_t>(kMaxLeAdvDataLength))));
}
void LEDataSetConnectionState() {
if (peer_.connectable()) {
peer_.MutLe().SetConnectionState(MakeConnectionState());
}
}
void LEDataSetConnectionParameters() {
if (!peer_.connectable()) {
return;
}
const hci::LEConnectionParameters conn_params(fdp().ConsumeIntegral<uint16_t>(),
fdp().ConsumeIntegral<uint16_t>(),
fdp().ConsumeIntegral<uint16_t>());
peer_.MutLe().SetConnectionParameters(conn_params);
}
void LEDataSetPreferredConnectionParameters() {
if (!peer_.connectable()) {
return;
}
const hci::LEPreferredConnectionParameters conn_params(
fdp().ConsumeIntegral<uint16_t>(), fdp().ConsumeIntegral<uint16_t>(),
fdp().ConsumeIntegral<uint16_t>(), fdp().ConsumeIntegral<uint16_t>());
peer_.MutLe().SetPreferredConnectionParameters(conn_params);
}
void LEDataSetBondData() {
if (!peer_.connectable()) {
return;
}
sm::PairingData data;
auto do_if_fdp = [&](auto action) {
if (fdp().ConsumeBool()) {
action();
}
};
do_if_fdp([&] { data.identity_address = bt::testing::MakePublicDeviceAddress(fdp()); });
do_if_fdp([&] { data.local_ltk = MakeLtk(); });
do_if_fdp([&] { data.peer_ltk = MakeLtk(); });
do_if_fdp([&] { data.cross_transport_key = MakeLtk(); });
do_if_fdp([&] { data.irk = MakeKey(); });
do_if_fdp([&] { data.csrk = MakeKey(); });
peer_.MutLe().SetBondData(data);
}
void LEDataClearBondData() {
if (!peer_.le() || !peer_.le()->bonded()) {
return;
}
peer_.MutLe().ClearBondData();
}
void LEDataSetFeatures() {
hci::LESupportedFeatures features = {};
fdp().ConsumeData(&features, sizeof(features));
peer_.MutLe().SetFeatures(features);
}
void LEDataSetServiceChangedGattData() {
peer_.MutLe().set_service_changed_gatt_data({fdp().ConsumeBool(), fdp().ConsumeBool()});
}
void LEDataSetAutoConnectBehavior() {
peer_.MutLe().set_auto_connect_behavior(fdp().PickValueInArray(
{Peer::AutoConnectBehavior::kAlways, Peer::AutoConnectBehavior::kSkipUntilNextConnection}));
}
template <typename T>
void BrEdrDataSetInquiryData() {
if (!peer_.identity_known()) {
return;
}
T inquiry_data = {};
fdp().ConsumeData(&inquiry_data, sizeof(inquiry_data));
inquiry_data.bd_addr = peer_.address().value();
peer_.MutBrEdr().SetInquiryData(inquiry_data);
}
void BrEdrDataSetConnectionState() {
if (!peer_.identity_known() || !peer_.connectable()) {
return;
}
peer_.MutBrEdr().SetConnectionState(MakeConnectionState());
}
void BrEdrDataSetBondData() {
if (!peer_.identity_known() || !peer_.connectable()) {
return;
}
peer_.MutBrEdr().SetBondData(MakeLtk());
}
void BrEdrDataClearBondData() {
if (!peer_.bredr() || !peer_.bredr()->bonded()) {
return;
}
peer_.MutBrEdr().ClearBondData();
}
void BrEdrDataAddService() {
if (!peer_.identity_known() || !peer_.connectable()) {
return;
}
UUID uuid;
fdp().ConsumeData(&uuid, sizeof(uuid));
peer_.MutBrEdr().AddService(uuid);
}
void SetName() { peer_.SetName(fdp().ConsumeRandomLengthString()); }
void SetFeaturePage() {
peer_.SetFeaturePage(
fdp().ConsumeIntegralInRange<size_t>(0, bt::hci::LMPFeatureSet::kMaxLastPageNumber),
fdp().ConsumeIntegral<uint64_t>());
}
void set_last_page_number() { peer_.set_last_page_number(fdp().ConsumeIntegral<uint8_t>()); }
void set_version() {
peer_.set_version(bt::hci::HCIVersion{fdp().ConsumeIntegral<uint8_t>()},
fdp().ConsumeIntegral<uint16_t>(), fdp().ConsumeIntegral<uint16_t>());
}
void set_identity_known() { peer_.set_identity_known(fdp().ConsumeBool()); }
void StoreBrEdrCrossTransportKey() {
if (!peer_.identity_known()) {
return;
}
if (!peer_.connectable()) {
return;
}
peer_.StoreBrEdrCrossTransportKey(MakeLtk());
}
void set_connectable() {
// It doesn't make sense to make a peer unconnectable and it fires lots of asserts.
peer_.set_connectable(true);
}
private:
FuzzedDataProvider &fdp() { return fuzzed_data_provider_; }
Peer::ConnectionState MakeConnectionState() {
return fdp().PickValueInArray({Peer::ConnectionState::kNotConnected,
Peer::ConnectionState::kInitializing,
Peer::ConnectionState::kConnected});
}
sm::Key MakeKey() {
// Actual value of the key is not fuzzed.
return sm::Key(MakeSecurityProperties(), {});
}
sm::LTK MakeLtk() {
// Actual value of the key is not fuzzed.
return sm::LTK(MakeSecurityProperties(), {});
}
sm::SecurityProperties MakeSecurityProperties() {
sm::SecurityProperties security(
fdp().ConsumeBool(), fdp().ConsumeBool(), fdp().ConsumeBool(),
fdp().ConsumeIntegralInRange<size_t>(0, sm::kMaxEncryptionKeySize));
return security;
}
FuzzedDataProvider &fuzzed_data_provider_;
Peer &peer_;
};
} // namespace gap::testing
} // namespace bt
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_TESTING_PEER_FUZZER_H_