[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;
+}