Commit f2d85904 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Luiz Augusto von Dentz says:

====================
bluetooth pull request for net:

 - mediatek: mt8183-pico6: Fix bluetooth node
 - sco: Fix use-after-free bugs caused by sco_sock_timeout
 - l2cap: fix null-ptr-deref in l2cap_chan_timeout
 - qca: Various fixes
 - l2cap: Fix slab-use-after-free in l2cap_connect()
 - msft: fix slab-use-after-free in msft_do_close()
 - HCI: Fix potential null-ptr-deref

* tag 'for-net-2024-05-03' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: qca: fix firmware check error path
  Bluetooth: l2cap: fix null-ptr-deref in l2cap_chan_timeout
  Bluetooth: HCI: Fix potential null-ptr-deref
  arm64: dts: mediatek: mt8183-pico6: Fix bluetooth node
  Bluetooth: qca: fix info leak when fetching board id
  Bluetooth: qca: fix info leak when fetching fw build id
  Bluetooth: qca: generalise device address check
  Bluetooth: qca: fix NVM configuration parsing
  Bluetooth: qca: add missing firmware sanity checks
  Bluetooth: msft: fix slab-use-after-free in msft_do_close()
  Bluetooth: L2CAP: Fix slab-use-after-free in l2cap_connect()
  Bluetooth: qca: fix wcn3991 device address check
  Bluetooth: Fix use-after-free bugs caused by sco_sock_timeout
====================

Link: https://lore.kernel.org/r/20240503171933.3851244-1-luiz.dentz@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents e0863634 40d442f9
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -82,7 +82,8 @@ pins-clk {
};

&mmc1 {
	bt_reset: bt-reset {
	bluetooth@2 {
		reg = <2>;
		compatible = "mediatek,mt7921s-bluetooth";
		pinctrl-names = "default";
		pinctrl-0 = <&bt_pins_reset>;
+92 −18
Original line number Diff line number Diff line
@@ -15,8 +15,6 @@

#define VERSION "0.1"

#define QCA_BDADDR_DEFAULT (&(bdaddr_t) {{ 0xad, 0x5a, 0x00, 0x00, 0x00, 0x00 }})

int qca_read_soc_version(struct hci_dev *hdev, struct qca_btsoc_version *ver,
			 enum qca_btsoc_type soc_type)
{
@@ -101,7 +99,8 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
{
	struct sk_buff *skb;
	struct edl_event_hdr *edl;
	char cmd, build_label[QCA_FW_BUILD_VER_LEN];
	char *build_label;
	char cmd;
	int build_lbl_len, err = 0;

	bt_dev_dbg(hdev, "QCA read fw build info");
@@ -116,6 +115,11 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
		return err;
	}

	if (skb->len < sizeof(*edl)) {
		err = -EILSEQ;
		goto out;
	}

	edl = (struct edl_event_hdr *)(skb->data);
	if (!edl) {
		bt_dev_err(hdev, "QCA read fw build info with no header");
@@ -131,14 +135,25 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
		goto out;
	}

	if (skb->len < sizeof(*edl) + 1) {
		err = -EILSEQ;
		goto out;
	}

	build_lbl_len = edl->data[0];
	if (build_lbl_len <= QCA_FW_BUILD_VER_LEN - 1) {
		memcpy(build_label, edl->data + 1, build_lbl_len);
		*(build_label + build_lbl_len) = '\0';

	if (skb->len < sizeof(*edl) + 1 + build_lbl_len) {
		err = -EILSEQ;
		goto out;
	}

	build_label = kstrndup(&edl->data[1], build_lbl_len, GFP_KERNEL);
	if (!build_label)
		goto out;

	hci_set_fw_info(hdev, "%s", build_label);

	kfree(build_label);
out:
	kfree_skb(skb);
	return err;
@@ -237,6 +252,11 @@ static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid)
		goto out;
	}

	if (skb->len < 3) {
		err = -EILSEQ;
		goto out;
	}

	*bid = (edl->data[1] << 8) + edl->data[2];
	bt_dev_dbg(hdev, "%s: bid = %x", __func__, *bid);

@@ -267,9 +287,10 @@ int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
}
EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd);

static void qca_tlv_check_data(struct hci_dev *hdev,
static int qca_tlv_check_data(struct hci_dev *hdev,
			       struct qca_fw_config *config,
		u8 *fw_data, enum qca_btsoc_type soc_type)
			       u8 *fw_data, size_t fw_size,
			       enum qca_btsoc_type soc_type)
{
	const u8 *data;
	u32 type_len;
@@ -279,12 +300,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
	struct tlv_type_patch *tlv_patch;
	struct tlv_type_nvm *tlv_nvm;
	uint8_t nvm_baud_rate = config->user_baud_rate;
	u8 type;

	config->dnld_mode = QCA_SKIP_EVT_NONE;
	config->dnld_type = QCA_SKIP_EVT_NONE;

	switch (config->type) {
	case ELF_TYPE_PATCH:
		if (fw_size < 7)
			return -EINVAL;

		config->dnld_mode = QCA_SKIP_EVT_VSE_CC;
		config->dnld_type = QCA_SKIP_EVT_VSE_CC;

@@ -293,6 +318,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
		bt_dev_dbg(hdev, "File version      : 0x%x", fw_data[6]);
		break;
	case TLV_TYPE_PATCH:
		if (fw_size < sizeof(struct tlv_type_hdr) + sizeof(struct tlv_type_patch))
			return -EINVAL;

		tlv = (struct tlv_type_hdr *)fw_data;
		type_len = le32_to_cpu(tlv->type_len);
		tlv_patch = (struct tlv_type_patch *)tlv->data;
@@ -332,25 +360,64 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
		break;

	case TLV_TYPE_NVM:
		if (fw_size < sizeof(struct tlv_type_hdr))
			return -EINVAL;

		tlv = (struct tlv_type_hdr *)fw_data;

		type_len = le32_to_cpu(tlv->type_len);
		length = (type_len >> 8) & 0x00ffffff;
		length = type_len >> 8;
		type = type_len & 0xff;

		BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
		/* Some NVM files have more than one set of tags, only parse
		 * the first set when it has type 2 for now. When there is
		 * more than one set there is an enclosing header of type 4.
		 */
		if (type == 4) {
			if (fw_size < 2 * sizeof(struct tlv_type_hdr))
				return -EINVAL;

			tlv++;

			type_len = le32_to_cpu(tlv->type_len);
			length = type_len >> 8;
			type = type_len & 0xff;
		}

		BT_DBG("TLV Type\t\t : 0x%x", type);
		BT_DBG("Length\t\t : %d bytes", length);

		if (type != 2)
			break;

		if (fw_size < length + (tlv->data - fw_data))
			return -EINVAL;

		idx = 0;
		data = tlv->data;
		while (idx < length) {
		while (idx < length - sizeof(struct tlv_type_nvm)) {
			tlv_nvm = (struct tlv_type_nvm *)(data + idx);

			tag_id = le16_to_cpu(tlv_nvm->tag_id);
			tag_len = le16_to_cpu(tlv_nvm->tag_len);

			if (length < idx + sizeof(struct tlv_type_nvm) + tag_len)
				return -EINVAL;

			/* Update NVM tags as needed */
			switch (tag_id) {
			case EDL_TAG_ID_BD_ADDR:
				if (tag_len != sizeof(bdaddr_t))
					return -EINVAL;

				memcpy(&config->bdaddr, tlv_nvm->data, sizeof(bdaddr_t));

				break;

			case EDL_TAG_ID_HCI:
				if (tag_len < 3)
					return -EINVAL;

				/* HCI transport layer parameters
				 * enabling software inband sleep
				 * onto controller side.
@@ -366,6 +433,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
				break;

			case EDL_TAG_ID_DEEP_SLEEP:
				if (tag_len < 1)
					return -EINVAL;

				/* Sleep enable mask
				 * enabling deep sleep feature on controller.
				 */
@@ -374,14 +444,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
				break;
			}

			idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len);
			idx += sizeof(struct tlv_type_nvm) + tag_len;
		}
		break;

	default:
		BT_ERR("Unknown TLV type %d", config->type);
		break;
		return -EINVAL;
	}

	return 0;
}

static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
@@ -531,7 +603,9 @@ static int qca_download_firmware(struct hci_dev *hdev,
	memcpy(data, fw->data, size);
	release_firmware(fw);

	qca_tlv_check_data(hdev, config, data, soc_type);
	ret = qca_tlv_check_data(hdev, config, data, size, soc_type);
	if (ret)
		goto out;

	segment = data;
	remain = size;
@@ -614,7 +688,7 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
}
EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);

static int qca_check_bdaddr(struct hci_dev *hdev)
static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *config)
{
	struct hci_rp_read_bd_addr *bda;
	struct sk_buff *skb;
@@ -638,7 +712,7 @@ static int qca_check_bdaddr(struct hci_dev *hdev)
	}

	bda = (struct hci_rp_read_bd_addr *)skb->data;
	if (!bacmp(&bda->bdaddr, QCA_BDADDR_DEFAULT))
	if (!bacmp(&bda->bdaddr, &config->bdaddr))
		set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);

	kfree_skb(skb);
@@ -667,7 +741,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
		   enum qca_btsoc_type soc_type, struct qca_btsoc_version ver,
		   const char *firmware_name)
{
	struct qca_fw_config config;
	struct qca_fw_config config = {};
	int err;
	u8 rom_ver = 0;
	u32 soc_ver;
@@ -852,7 +926,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
		break;
	}

	err = qca_check_bdaddr(hdev);
	err = qca_check_bdaddr(hdev, &config);
	if (err)
		return err;

+2 −1
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#define EDL_PATCH_CONFIG_RES_EVT	(0x00)
#define QCA_DISABLE_LOGGING_SUB_OP	(0x14)

#define EDL_TAG_ID_BD_ADDR		2
#define EDL_TAG_ID_HCI			(17)
#define EDL_TAG_ID_DEEP_SLEEP		(27)

@@ -47,7 +48,6 @@
#define get_soc_ver(soc_id, rom_ver)	\
	((le32_to_cpu(soc_id) << 16) | (le16_to_cpu(rom_ver)))

#define QCA_FW_BUILD_VER_LEN		255
#define QCA_HSP_GF_SOC_ID			0x1200
#define QCA_HSP_GF_SOC_MASK			0x0000ff00

@@ -94,6 +94,7 @@ struct qca_fw_config {
	uint8_t user_baud_rate;
	enum qca_tlv_dnld_mode dnld_mode;
	enum qca_tlv_dnld_mode dnld_type;
	bdaddr_t bdaddr;
};

struct edl_event_hdr {
+1 −2
Original line number Diff line number Diff line
@@ -2768,8 +2768,6 @@ void hci_unregister_dev(struct hci_dev *hdev)

	hci_unregister_suspend_notifier(hdev);

	msft_unregister(hdev);

	hci_dev_do_close(hdev);

	if (!test_bit(HCI_INIT, &hdev->flags) &&
@@ -2823,6 +2821,7 @@ void hci_release_dev(struct hci_dev *hdev)
	hci_discovery_filter_clear(hdev);
	hci_blocked_keys_clear(hdev);
	hci_codec_list_clear(&hdev->local_codecs);
	msft_release(hdev);
	hci_dev_unlock(hdev);

	ida_destroy(&hdev->unset_handle_ida);
+2 −0
Original line number Diff line number Diff line
@@ -7037,6 +7037,8 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
			u16 handle = le16_to_cpu(ev->bis[i]);

			bis = hci_conn_hash_lookup_handle(hdev, handle);
			if (!bis)
				continue;

			set_bit(HCI_CONN_BIG_SYNC_FAILED, &bis->flags);
			hci_connect_cfm(bis, ev->status);
Loading