// Copyright 2023 The Pigweed Authors // // 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 // // https://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 "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" #include "pw_bluetooth_sapphire/internal/host/common/random.h" #include "pw_bluetooth_sapphire/internal/host/l2cap/channel.h" #include "pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h" #include "pw_bluetooth_sapphire/internal/host/testing/controller_test.h" #include "pw_bluetooth_sapphire/internal/host/testing/controller_test_double_base.h" namespace bt::testing { // ACL Buffer Info constexpr size_t kMaxDataPacketLength = 64; // Ensure outbound ACL packets aren't queued. constexpr size_t kBufferMaxNumPackets = 1000; // If the packet size is too large, we consume too much of the fuzzer data per // packet without much benefit. constexpr uint16_t kMaxAclPacketSize = 100; constexpr hci_spec::ConnectionHandle kHandle = 0x0001; // Don't toggle connection too often or else l2cap won't get very far. constexpr float kToggleConnectionChance = 0.04f; class FuzzerController : public ControllerTestDoubleBase, public WeakSelf { public: explicit FuzzerController(pw::async::Dispatcher& pw_dispatcher) : ControllerTestDoubleBase(pw_dispatcher), WeakSelf(this) {} ~FuzzerController() override = default; private: // Controller overrides: void SendCommand( [[maybe_unused]] pw::span command) override {} void SendAclData([[maybe_unused]] pw::span data) override {} void SendScoData([[maybe_unused]] pw::span data) override {} void SendIsoData([[maybe_unused]] pw::span data) override {} }; // Reuse ControllerTest test fixture code even though we're not using gtest. using TestingBase = FakeDispatcherControllerTest; class DataFuzzTest : public TestingBase { public: DataFuzzTest(const uint8_t* data, size_t size) : data_(data, size), rng_(&data_) { set_random_generator(&rng_); TestingBase::SetUp(); const auto bredr_buffer_info = hci::DataBufferInfo(kMaxDataPacketLength, kBufferMaxNumPackets); InitializeACLDataChannel(bredr_buffer_info); channel_manager_ = l2cap::ChannelManager::Create(transport()->acl_data_channel(), transport()->command_channel(), /*random_channel_ids=*/true, dispatcher()); } ~DataFuzzTest() override { channel_manager_ = nullptr; bt::set_random_generator(nullptr); TestingBase::TearDown(); } void TestBody() override { RegisterService(); while (data_.remaining_bytes() > 0) { bool run_loop = data_.ConsumeBool(); if (run_loop) { RunUntilIdle(); } if (!SendAclPacket()) { break; } if (data_.ConsumeProbability() < kToggleConnectionChance) { ToggleConnection(); } } RunUntilIdle(); } bool SendAclPacket() { if (data_.remaining_bytes() < sizeof(uint64_t)) { return false; } // Consumes 8 bytes. auto packet_size = data_.ConsumeIntegralInRange( sizeof(hci_spec::ACLDataHeader), static_cast(std::min(static_cast(kMaxAclPacketSize), data_.remaining_bytes()))); auto packet_data = data_.ConsumeBytes(packet_size); if (packet_data.size() < packet_size) { // Check if we ran out of fuzzer data. return false; } MutableBufferView packet_view(packet_data.data(), packet_data.size()); // Use correct length so packets aren't rejected for invalid length. packet_view.AsMutable()->data_total_length = pw::bytes::ConvertOrderTo( cpp20::endian::little, static_cast( (packet_view.size() - sizeof(hci_spec::ACLDataHeader)))); // Use correct connection handle so packets aren't rejected/queued for // invalid handle. uint16_t handle_and_flags = packet_view.ReadMember<&hci_spec::ACLDataHeader::handle_and_flags>(); handle_and_flags &= 0xF000; // Keep flags, clear handle. handle_and_flags |= kHandle; packet_view.AsMutable()->handle_and_flags = handle_and_flags; PW_CHECK(test_device()->SendACLDataChannelPacket(packet_view)); return true; } void RegisterService() { channel_manager_->RegisterService( l2cap::kAVDTP, l2cap::ChannelParameters(), [this](l2cap::Channel::WeakPtr chan) { if (!chan.is_alive()) { return; } chan->Activate(/*rx_callback=*/[](auto) {}, /*closed_callback=*/ [this, id = chan->id()] { channels_.erase(id); }); channels_.emplace(chan->id(), std::move(chan)); }); } void ToggleConnection() { if (connection_) { channel_manager_->RemoveConnection(kHandle); connection_ = false; return; } channel_manager_->AddACLConnection( kHandle, pw::bluetooth::emboss::ConnectionRole::CENTRAL, /*link_error_callback=*/[] {}, /*security_callback=*/[](auto, auto, auto) {}, /*fixed_channels_callback=*/ [](l2cap::ChannelManager::BrEdrFixedChannels) {}); connection_ = true; } private: FuzzedDataProvider data_; pw::random::FuzzerRandomGenerator rng_; std::unique_ptr channel_manager_; bool connection_ = false; std::unordered_map channels_; }; } // namespace bt::testing extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { bt::testing::DataFuzzTest fuzz(data, size); fuzz.TestBody(); return 0; }