[bluetooth] Add a fuzzer: parsing acl data packet.
I have to reformat some of the code to abstract out the
function that is easy to fuzz.
Tests: Manually run the fuzzer.
Change-Id: Ie23d8c2e39709560a6d6c3d4fb52f2c2867c931b
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/402818
Commit-Queue: Yu Shan <shanyu@google.com>
Reviewed-by: Ben Lawson <benlawson@google.com>
Testability-Review: Ben Lawson <benlawson@google.com>
diff --git a/src/connectivity/bluetooth/core/bt-host/BUILD.gn b/src/connectivity/bluetooth/core/bt-host/BUILD.gn
index c34d23bf..e909973 100644
--- a/src/connectivity/bluetooth/core/bt-host/BUILD.gn
+++ b/src/connectivity/bluetooth/core/bt-host/BUILD.gn
@@ -166,5 +166,6 @@
"l2cap:common_handler_fuzzer",
"sdp:data_element_fuzzer",
"sdp:pdu_fuzzer",
+ "hci:read_acl_data_packet_fuzzer",
]
}
diff --git a/src/connectivity/bluetooth/core/bt-host/hci/BUILD.gn b/src/connectivity/bluetooth/core/bt-host/hci/BUILD.gn
index f494347..1f4082e 100644
--- a/src/connectivity/bluetooth/core/bt-host/hci/BUILD.gn
+++ b/src/connectivity/bluetooth/core/bt-host/hci/BUILD.gn
@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build/fuzzing/fuzzer.gni")
+
# Basic target with protocol definitions and no logic, suitable for test
# emulation.
source_set("definitions") {
@@ -117,3 +119,11 @@
"//third_party/googletest:gtest",
]
}
+
+fuzzer("read_acl_data_packet_fuzzer") {
+ sources = [ "acl_data_channel_read_acl_data_packet_fuzztest.cc" ]
+ deps = [
+ ":testing",
+ "//src/connectivity/bluetooth/core/bt-host/testing",
+ ]
+}
diff --git a/src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.cc b/src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.cc
index 362de8e..13f6245 100644
--- a/src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.cc
+++ b/src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.cc
@@ -474,36 +474,12 @@
bt_log(ERROR, "hci", "failed to allocate buffer received ACL data packet!");
return;
}
- uint32_t read_size;
- auto packet_bytes = packet->mutable_view()->mutable_data();
- zx_status_t read_status = channel_.read(0u, packet_bytes.mutable_data(), nullptr,
- packet_bytes.size(), 0, &read_size, nullptr);
- if (read_status < 0) {
- bt_log(DEBUG, "hci", "failed to read RX bytes: %s", zx_status_get_string(status));
- // Clear the handler so that we stop receiving events from it.
- // TODO(jamuraa): signal failure to the consumer so it can do something.
+ zx_status_t status = ReadACLDataPacketFromChannel(channel_, packet);
+ if (status == ZX_ERR_INVALID_ARGS) {
+ continue;
+ } else if (status != ZX_OK) {
return;
}
-
- if (read_size < sizeof(ACLDataHeader)) {
- bt_log(ERROR, "hci", "malformed data packet - expected at least %zu bytes, got %u",
- sizeof(ACLDataHeader), read_size);
- // TODO(jamuraa): signal stream error somehow
- continue;
- }
-
- const size_t rx_payload_size = read_size - sizeof(ACLDataHeader);
- const size_t size_from_header = le16toh(packet->view().header().data_total_length);
- if (size_from_header != rx_payload_size) {
- bt_log(ERROR, "hci",
- "malformed packet - payload size from header (%zu) does not match"
- " received payload size: %zu",
- size_from_header, rx_payload_size);
- // TODO(jamuraa): signal stream error somehow
- continue;
- }
-
- packet->InitializeFromBuffer();
{
TRACE_DURATION("bluetooth", "ACLDataChannel->rx_callback_");
rx_callback_(std::move(packet));
@@ -516,6 +492,41 @@
}
}
+zx_status_t ACLDataChannel::ReadACLDataPacketFromChannel(const zx::channel &channel,
+ const ACLDataPacketPtr& packet) {
+ uint32_t read_size;
+ auto packet_bytes = packet->mutable_view()->mutable_data();
+ zx_status_t read_status = channel.read(0u, packet_bytes.mutable_data(), nullptr,
+ packet_bytes.size(), 0, &read_size, nullptr);
+ if (read_status < 0) {
+ bt_log(DEBUG, "hci", "failed to read RX bytes: %s", zx_status_get_string(read_status));
+ // Clear the handler so that we stop receiving events from it.
+ // TODO(jamuraa): signal failure to the consumer so it can do something.
+ return ZX_ERR_IO;
+ }
+
+ if (read_size < sizeof(ACLDataHeader)) {
+ bt_log(ERROR, "hci", "malformed data packet - expected at least %zu bytes, got %u",
+ sizeof(ACLDataHeader), read_size);
+ // TODO(jamuraa): signal stream error somehow
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ const size_t rx_payload_size = read_size - sizeof(ACLDataHeader);
+ const size_t size_from_header = le16toh(packet->view().header().data_total_length);
+ if (size_from_header != rx_payload_size) {
+ bt_log(ERROR, "hci",
+ "malformed packet - payload size from header (%zu) does not match"
+ " received payload size: %zu",
+ size_from_header, rx_payload_size);
+ // TODO(jamuraa): signal stream error somehow
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ packet->InitializeFromBuffer();
+ return ZX_OK;
+}
+
ACLDataChannel::DataPacketQueue::iterator ACLDataChannel::SendQueueInsertLocationForPriority(
PacketPriority priority) {
// insert low priority packets at the end of the queue
diff --git a/src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.h b/src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.h
index 5323a63..859d2d9 100644
--- a/src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.h
+++ b/src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.h
@@ -178,6 +178,13 @@
// dedicated LE buffer.
const DataBufferInfo& GetLEBufferInfo() const;
+ // Reads bytes from the channel and try to parse them as ACLDataPacket.
+ // ZX_ERR_IO means error happens while reading from the channel.
+ // ZX_ERR_INVALID_ARGS means the packet is malformed.
+ // Otherwise, ZX_OK is returned.
+ static zx_status_t ReadACLDataPacketFromChannel(const zx::channel &channel,
+ const ACLDataPacketPtr& packet);
+
private:
// Represents a queued ACL data packet.
struct QueuedDataPacket {
diff --git a/src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel_read_acl_data_packet_fuzztest.cc b/src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel_read_acl_data_packet_fuzztest.cc
new file mode 100644
index 0000000..a3c3f71
--- /dev/null
+++ b/src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel_read_acl_data_packet_fuzztest.cc
@@ -0,0 +1,33 @@
+#include "src/connectivity/bluetooth/core/bt-host/hci/acl_data_channel.h"
+
+#include "slab_allocators.h"
+
+// Prevent "undefined symbol: __zircon_driver_rec__" error.
+BT_DECLARE_FAKE_DRIVER();
+
+namespace bt::hci {
+
+void fuzz(const uint8_t* data, size_t size) {
+ // Allocate a buffer for the event. Since we don't know the size beforehand
+ // we allocate the largest possible buffer.
+ auto packet = ACLDataPacket::New(slab_allocators::kLargeACLDataPayloadSize);
+ if (!packet) {
+ bt_log(ERROR, "hci", "failed to allocate buffer received ACL data packet!");
+ return;
+ }
+ zx::channel a;
+ zx::channel b;
+ zx_status_t status = zx::channel::create(0u, &a, &b);
+ if (status != ZX_OK) {
+ return;
+ }
+ a.write(0u, data, size, nullptr, 0);
+ ACLDataChannel::ReadACLDataPacketFromChannel(b, packet);
+}
+
+} // namespace bt::hci
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ bt::hci::fuzz(data, size);
+ return 0;
+}