Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 84a20dac authored by Xuan Xing's avatar Xuan Xing Committed by Hui Peng
Browse files

Adding a Bluetooth SDP stack fuzzer

Test: Manually build the SDP fuzzer

Bug: 274525374
Bug: 276759518
Merged-In: Id12dd5d922f3eb71081413323b1e22bb1780c2df
Change-Id: Id12dd5d922f3eb71081413323b1e22bb1780c2df
parent b2a230f4
Loading
Loading
Loading
Loading
+80 −0
Original line number Diff line number Diff line
@@ -297,6 +297,86 @@ cc_library_static {
    min_sdk_version: "Tiramisu",
}

cc_defaults {
    name: "btstack_fuzzer_default",
    host_supported: true,
    cflags: [
        "-Wall",
        "-Wextra",
        "-Werror",
        // Mocked components have too many unused parameters
        "-Wno-unused-parameter",
        "-DHAS_NO_BDROID_BUILDCFG",
    ],
    include_dirs: [
        "packages/modules/Bluetooth/system",
        "packages/modules/Bluetooth/system/gd",
        "packages/modules/Bluetooth/system/include",
        "packages/modules/Bluetooth/system/internal_include",
        "packages/modules/Bluetooth/system/stack/include",
        "packages/modules/Bluetooth/system/test/common",
        "packages/modules/Bluetooth/system/types",
    ],
    static_libs: [
        "libchrome",
    ],
    target: {
        darwin: {
            enabled: false,
        },
        android: {
            shared_libs: [
                "libcutils",
                "libutils",
            ],
        },
        host: {
            cflags: [
                "-DOS_GENERIC",
            ],
        },
    },
}

cc_fuzz {
    name: "sdp-fuzzer",
    defaults: [
        "btstack_fuzzer_default",
        "fluoride_defaults_fuzzable",
    ],
    include_dirs: [
        "packages/modules/Bluetooth/system/stack/btm",
    ],
    srcs: [
        ":LegacyStackSdp",
        ":TestCommonLogMsg",
        ":TestCommonMockFunctions",
        ":TestFakeOsi",
        ":TestMockBtif",
        ":TestMockDevice",
        ":TestMockGdOsLoggingLogRedaction",
        ":TestMockStackL2cap",
        ":TestMockStackMetrics",
        ":TestMockStackBtm",
        "fuzzers/sdp_fuzzer.cc",
    ],
    fuzz_config: {
        cc: [
            "android-bluetooth-security@google.com",
            "android-security-assurance-redteam@google.com",
        ],
        componentid: 27441,
        hotlists: [
            "4810445",
            "3705175",
        ],
        acknowledgement: [
            "Android Red Team of Google",
            "Android Bluetooth Team of Google",
        ],
    },
}

// Bluetooth stack unit tests for target
cc_test {
    name: "net_test_stack",
+252 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 The Android Open Source Project
 *
 * 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
 *
 *      http://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 <cstdint>
#include <string>
#include <vector>

#include "osi/include/allocator.h"
#include "stack/include/bt_hdr.h"
#include "stack/include/sdp_api.h"
#include "stack/include/sdpdefs.h"
#include "stack/sdp/sdpint.h"
#include "test/fake/fake_osi.h"
#include "test/mock/mock_btif_config.h"
#include "test/mock/mock_stack_l2cap_api.h"
#include "types/bluetooth/uuid.h"

namespace {

#define SDP_DB_SIZE 0x10000

constexpr uint16_t kDummyCID = 0x1234;
constexpr uint16_t kDummyPSM = 0x7788;
constexpr uint8_t kDummyID = 0x99;
constexpr uint8_t kDummyAddr[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};

// Set up default callback structure
tL2CAP_APPL_INFO cb_info = {
    .pL2CA_ConnectInd_Cb = [](const RawAddress& bd_addr, uint16_t lcid,
                              uint16_t psm,
                              uint8_t id) {},  // tL2CA_CONNECT_IND_CB
    .pL2CA_ConnectCfm_Cb = [](uint16_t lcid,
                              uint16_t result) {},  // tL2CA_CONNECT_CFM_CB
    .pL2CA_ConfigInd_Cb = [](uint16_t lcid,
                             tL2CAP_CFG_INFO* p_cfg) {},  // tL2CA_CONFIG_IND_CB
    .pL2CA_ConfigCfm_Cb = [](uint16_t lcid, uint16_t initiator,
                             tL2CAP_CFG_INFO* p_cfg) {},  // tL2CA_CONFIG_CFM_CB
    .pL2CA_DisconnectInd_Cb =
        [](uint16_t lcid, bool should_ack) {},  // tL2CA_DISCONNECT_IND_CB
    .pL2CA_DisconnectCfm_Cb =
        [](uint16_t lcid, uint16_t result) {},  // tL2CA_DISCONNECT_CFM_CB
    .pL2CA_DataInd_Cb = [](uint16_t lcid,
                           BT_HDR* data) {},  // tL2CA_DATA_IND_CB
    .pL2CA_CongestionStatus_Cb =
        [](uint16_t lcid, bool is_congested) {},  // tL2CA_CONGESTION_STATUS_CB
    .pL2CA_TxComplete_Cb = [](uint16_t lcid,
                              uint16_t num_sdu) {},  // tL2CA_TX_COMPLETE_CB
    .pL2CA_Error_Cb = [](uint16_t lcid,
                         uint16_t error_type) {},  // tL2CA_ERROR_CB
    .pL2CA_CreditBasedConnectInd_Cb =
        [](const RawAddress& bdaddr, std::vector<uint16_t>& lcids, uint16_t psm,
           uint16_t peer_mtu,
           uint8_t identifier) {},  // tL2CA_CREDIT_BASED_CONNECT_IND_CB
    .pL2CA_CreditBasedConnectCfm_Cb =
        [](const RawAddress& bdaddr, uint16_t lcid, uint16_t peer_mtu,
           uint16_t result) {},  // tL2CA_CREDIT_BASED_CONNECT_CFM_CB
    .pL2CA_CreditBasedReconfigCompleted_Cb =
        [](const RawAddress& bdaddr, uint16_t lcid, bool is_local_cfg,
           tL2CAP_LE_CFG_INFO* p_cfg) {
        },  // tL2CA_CREDIT_BASED_RECONFIG_COMPLETED_CB
    .pL2CA_CreditBasedCollisionInd_Cb =
        [](const RawAddress& bdaddr) {},  // tL2CA_CREDIT_BASED_COLLISION_IND_CB
};

class FakeL2cap {
 public:
  FakeL2cap() {
    test::mock::stack_l2cap_api::L2CA_ConnectReq.body =
        [](uint16_t psm, const RawAddress& raw_address) { return kDummyCID; };
    test::mock::stack_l2cap_api::L2CA_ConnectReq2.body =
        [](uint16_t psm, const RawAddress& p_bd_addr, uint16_t sec_level) {
          return L2CA_ConnectReq(psm, p_bd_addr);
        };
    test::mock::stack_l2cap_api::L2CA_DataWrite.body = [](uint16_t cid,
                                                          BT_HDR* p_data) {
      auto len = p_data->len;
      osi_free(p_data);
      return (uint8_t)len;
    };
    test::mock::stack_l2cap_api::L2CA_DisconnectReq.body = [](uint16_t lcid) {
      return true;
    };
    test::mock::stack_l2cap_api::L2CA_Register2.body =
        [](uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info, bool enable_snoop,
           tL2CAP_ERTM_INFO* p_ertm_info, uint16_t my_mtu,
           uint16_t required_remote_mtu, uint16_t sec_level) {
          cb_info = p_cb_info;
          return psm;
        };
  }

  ~FakeL2cap() {
    test::mock::stack_l2cap_api::L2CA_ConnectReq = {};
    test::mock::stack_l2cap_api::L2CA_ConnectReq2 = {};
    test::mock::stack_l2cap_api::L2CA_DataWrite = {};
    test::mock::stack_l2cap_api::L2CA_DisconnectReq = {};
    test::mock::stack_l2cap_api::L2CA_Register2 = {};
  }
};

class FakeBtifConfig {
 public:
  FakeBtifConfig() {
    test::mock::btif_config::btif_config_set_bin.body =
        [](const std::string&, const std::string&, const uint8_t*, size_t) {
          // This function is not properly mocked. The abort here allows us to
          // catch any cases using this mock.
          abort();
          return true;
        };
    test::mock::btif_config::btif_config_set_int.body =
        [](const std::string& section, const std::string& key, int value) {
          // This function is not properly mocked. The abort here allows us to
          // catch any cases using this mock.
          abort();
          return true;
        };
  }

  ~FakeBtifConfig() {
    test::mock::btif_config::btif_config_set_bin = {};
    test::mock::btif_config::btif_config_set_int = {};
  }
};

class Fakes {
 public:
  test::fake::FakeOsi fake_osi;
  FakeL2cap fake_l2cap;
  FakeBtifConfig fake_btif_config;
};

}  // namespace

static void FuzzAsServer(const uint8_t* data, size_t size) {
  FuzzedDataProvider fdp(data, size);
  std::vector<std::vector<uint8_t>> attrs;

  sdp_init();
  auto rec_num = fdp.ConsumeIntegralInRange<uint8_t>(0, 10);
  for (uint8_t i = 0; i < rec_num; i++) {
    auto handle = SDP_CreateRecord();
    auto attr_num = fdp.ConsumeIntegralInRange<uint8_t>(0, 10);
    for (uint8_t s = 0; s < attr_num; s++) {
      auto id = (i == 0) ? ATTR_ID_BT_PROFILE_DESC_LIST
                         : fdp.ConsumeIntegral<uint16_t>();
      auto type = fdp.ConsumeIntegral<uint8_t>();
      auto len = fdp.ConsumeIntegralInRange<uint16_t>(1, 512);
      auto data = fdp.ConsumeBytes<uint8_t>(len);

      if (data.size() == 0) {
        break;
      }

      attrs.push_back(data);
      SDP_AddAttribute(handle, id, type, data.size(), data.data());
    }
  }

  cb_info.pL2CA_ConnectInd_Cb(RawAddress(kDummyAddr), kDummyCID, kDummyPSM,
                              kDummyID);

  tL2CAP_CFG_INFO cfg = {};
  cb_info.pL2CA_ConfigCfm_Cb(kDummyCID, 0, &cfg);

  while (fdp.remaining_bytes() > 0) {
    auto size = fdp.ConsumeIntegralInRange<uint16_t>(0, 1024);
    auto bytes = fdp.ConsumeBytes<uint8_t>(size);
    BT_HDR* hdr = (BT_HDR*)osi_calloc(sizeof(BT_HDR) + bytes.size());
    hdr->len = bytes.size();
    std::copy(bytes.cbegin(), bytes.cend(), hdr->data);
    cb_info.pL2CA_DataInd_Cb(kDummyCID, hdr);
  }

  cb_info.pL2CA_DisconnectInd_Cb(kDummyCID, false);
  sdp_free();
}

static void FuzzAsClient(const uint8_t* data, size_t size) {
  FuzzedDataProvider fdp(data, size);
  std::shared_ptr<tSDP_DISCOVERY_DB> p_db(
      (tSDP_DISCOVERY_DB*)malloc(SDP_DB_SIZE), free);

  std::vector<bluetooth::Uuid> init_uuids;
  std::vector<uint16_t> init_attrs;

  sdp_init();

  uint8_t num_uuid =
      fdp.ConsumeIntegralInRange<uint8_t>(0, SDP_MAX_UUID_FILTERS);
  uint8_t num_attr =
      fdp.ConsumeIntegralInRange<uint8_t>(0, SDP_MAX_ATTR_FILTERS);

  for (uint8_t i = 0; i < num_uuid; i++) {
    init_uuids.push_back(
        bluetooth::Uuid::From16Bit(fdp.ConsumeIntegral<uint16_t>()));
  }

  for (uint8_t i = 0; i < num_attr; i++) {
    init_attrs.push_back(fdp.ConsumeIntegral<uint16_t>());
  }

  SDP_InitDiscoveryDb(p_db.get(), SDP_DB_SIZE, init_uuids.size(),
                      init_uuids.data(), init_attrs.size(), init_attrs.data());

  bool is_di_discover = fdp.ConsumeBool();
  if (is_di_discover) {
    SDP_ServiceSearchRequest(kDummyAddr, p_db.get(), [](tSDP_RESULT result) {});
  } else {
    SDP_ServiceSearchAttributeRequest(kDummyAddr, p_db.get(),
                                      [](tSDP_RESULT result) {});
  }
  cb_info.pL2CA_ConnectCfm_Cb(kDummyCID, L2CAP_CONN_OK);

  tL2CAP_CFG_INFO cfg = {};
  cb_info.pL2CA_ConfigCfm_Cb(kDummyCID, 0, &cfg);

  while (fdp.remaining_bytes() > 0) {
    auto size = fdp.ConsumeIntegralInRange<uint16_t>(0, 1024);
    auto bytes = fdp.ConsumeBytes<uint8_t>(size);
    BT_HDR* hdr = (BT_HDR*)osi_calloc(sizeof(BT_HDR) + bytes.size());
    hdr->len = bytes.size();
    std::copy(bytes.cbegin(), bytes.cend(), hdr->data);
    cb_info.pL2CA_DataInd_Cb(kDummyCID, hdr);
  }

  cb_info.pL2CA_DisconnectInd_Cb(kDummyCID, false);
  sdp_free();
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
  auto fakes = std::make_unique<Fakes>();

  FuzzAsServer(Data, Size);
  FuzzAsClient(Data, Size);
  return 0;
}