blob: 325d46c72742338267dbeb0c460de37ca8177dd4 [file] [log] [blame]
// 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 <fuzzer/FuzzedDataProvider.h>
#include <pw_async/fake_dispatcher.h>
#include <algorithm>
#include <limits>
#include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
#include "pw_bluetooth_sapphire/internal/host/l2cap/enhanced_retransmission_mode_engines.h"
#include "pw_bluetooth_sapphire/internal/host/l2cap/fragmenter.h"
#include "pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
constexpr static bt::hci_spec::ConnectionHandle kTestHandle = 0x0001;
constexpr bt::l2cap::ChannelId kTestChannelId = 0x0001;
void NoOpTxCallback(bt::ByteBufferPtr) {}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider provider(data, size);
pw::async::test::FakeDispatcher dispatcher;
uint8_t tx_window =
std::max(provider.ConsumeIntegral<uint8_t>(), static_cast<uint8_t>(1u));
uint8_t max_transmissions = provider.ConsumeIntegral<uint8_t>();
uint16_t max_tx_sdu_size =
std::max(provider.ConsumeIntegral<uint16_t>(), bt::l2cap::kMinACLMTU);
bool failure = false;
auto failure_cb = [&failure] { failure = true; };
auto [rx_engine, tx_engine] =
bt::l2cap::internal::MakeLinkedEnhancedRetransmissionModeEngines(
kTestChannelId,
max_tx_sdu_size,
max_transmissions,
tx_window,
NoOpTxCallback,
failure_cb,
dispatcher);
// In the real stack, the engines are shut down on failure, so we do the same
// here.
while (provider.remaining_bytes() > 0 && !failure) {
bool tx = provider.ConsumeBool();
if (tx) {
auto n_bytes = provider.ConsumeIntegral<uint16_t>();
auto bytes = provider.ConsumeBytes<uint8_t>(n_bytes);
tx_engine->QueueSdu(
std::make_unique<bt::DynamicByteBuffer>(bt::BufferView(bytes)));
} else {
bt::l2cap::Fragmenter fragmenter(kTestHandle);
auto n_bytes = provider.ConsumeIntegral<uint16_t>();
auto bytes = provider.ConsumeBytes<uint8_t>(n_bytes);
bool append_fcs = provider.ConsumeBool();
if (append_fcs) {
const size_t bounded_size =
std::min(bytes.size(),
std::numeric_limits<uint16_t>::max() -
sizeof(bt::l2cap::FrameCheckSequence));
bytes.resize(bounded_size);
}
auto fcs_option = append_fcs
? bt::l2cap::FrameCheckSequenceOption::kIncludeFcs
: bt::l2cap::FrameCheckSequenceOption::kNoFcs;
auto pdu = fragmenter.BuildFrame(
kTestChannelId, bt::BufferView(bytes), fcs_option);
rx_engine->ProcessPdu(std::move(pdu));
}
// Run for 0-255 seconds, which is enough to trigger poll timer and monitor
// timer.
auto run_duration =
std::chrono::seconds(provider.ConsumeIntegral<uint8_t>());
dispatcher.RunFor(run_duration);
}
return 0;
}