blob: 3e11b60da21ef2064b3e53654455d7f72aa821ac [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.
#include <fuzzer/FuzzedDataProvider.h>
#include "src/connectivity/bluetooth/core/bt-host/fidl/adapter_test_fixture.h"
#include "src/connectivity/bluetooth/core/bt-host/fidl/host_server.h"
#include "src/connectivity/bluetooth/core/bt-host/testing/peer_fuzzer.h"
namespace bthost {
namespace {
class HostServerFuzzTest final : public bthost::testing::AdapterTestFixture {
public:
HostServerFuzzTest() { SetUp(); }
~HostServerFuzzTest() override {
host_ = nullptr;
host_server_ = nullptr;
gatt_ = nullptr;
AdapterTestFixture::TearDown();
}
// Creates a peer with fuzzer-generated mutations that will be encoded as a
// fuchsia.bluetooth.sys.Peer and sent as a response to WatchPeers immediately (see
// FIDL_HostServerTest.WatchPeersRepliesOnFirstCallWithExistingPeers).
void FuzzWatchPeers(FuzzedDataProvider &fuzzed_data_provider) {
// WatchPeers only tracks connectable peers by design
bt::gap::Peer *const peer =
adapter()->peer_cache()->NewPeer(bt::testing::MakePublicDeviceAddress(fuzzed_data_provider),
/*connectable=*/true);
ZX_ASSERT(peer);
bt::gap::testing::PeerFuzzer peer_fuzzer(fuzzed_data_provider, *peer);
while (fuzzed_data_provider.remaining_bytes() != 0) {
peer_fuzzer.FuzzOneField();
}
// TODO(fxbug.dev/65461): WatchPeers will trigger this test as a failure if we try to encode a
// lot of peers, even though fuzzing multiple peers would be helpful.
int watch_peers_responses = 0;
host_client()->WatchPeers([this, peer, &watch_peers_responses](auto updated, auto removed) {
ZX_ASSERT_MSG(updated.size() == 1, "peer %s: peers updated = %zu", bt_str(*peer),
updated.size());
ZX_ASSERT_MSG(removed.size() == 0, "peer %s: peers removed = %zu", bt_str(*peer),
removed.size());
HandleWatchPeersResponse(*host_client(), watch_peers_responses, /*max_call_depth=*/1,
std::move(updated), std::move(removed));
});
RunLoopUntilIdle();
ZX_ASSERT_MSG(watch_peers_responses == 1, "peer %s: WatchPeers returned %d times",
bt_str(*peer), watch_peers_responses);
}
private:
HostServer *host_server() const { return host_server_.get(); }
fuchsia::bluetooth::host::Host *host_client() const { return host_.get(); }
void SetUp() override {
AdapterTestFixture::SetUp();
gatt_ = take_gatt();
fidl::InterfaceHandle<fuchsia::bluetooth::host::Host> host_handle;
host_server_ = std::make_unique<HostServer>(host_handle.NewRequest().TakeChannel(),
adapter()->AsWeakPtr(), gatt_->AsWeakPtr());
host_.Bind(std::move(host_handle));
}
// WatchPeers response handler that can re-initiate the call per the "hanging get" pattern up to
// |max_call_depth| times, like a normal client might.
template <typename Host>
void HandleWatchPeersResponse(Host &host, int &call_counter, int max_call_depth,
std::vector<fuchsia::bluetooth::sys::Peer> updated,
std::vector<fuchsia::bluetooth::PeerId> removed) {
call_counter++;
ZX_ASSERT_MSG(call_counter <= max_call_depth, "max depth (%d) exceeded", call_counter);
host.WatchPeers([this, &host, &call_counter, max_call_depth](auto updated, auto removed) {
this->HandleWatchPeersResponse(host, call_counter, max_call_depth, std::move(updated),
std::move(removed));
});
}
// No-op impl for the GoogleTest base class
void TestBody() override {}
std::unique_ptr<bt::gatt::GATT> gatt_;
std::unique_ptr<HostServer> host_server_;
fuchsia::bluetooth::host::HostPtr host_;
};
} // namespace
} // namespace bthost
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fuzzed_data_provider(data, size);
bthost::HostServerFuzzTest host_server_fuzz_test;
host_server_fuzz_test.FuzzWatchPeers(fuzzed_data_provider);
return 0;
}