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

====================
bluetooth 2022-11-02

 - Fix memory leak in hci_vhci driver
 - Fix handling of skb on virtio_bt driver
 - Fix accepting connection for invalid L2CAP PSM
 - Fix attemting to access uninitialized memory
 - Fix use-after-free in l2cap_reassemble_sdu
 - Fix use-after-free in l2cap_conn_del
 - Fix handling of destination address type for CIS
 - Fix not restoring ISO buffer count on disconnect

* tag 'for-net-2022-10-02' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: L2CAP: Fix attempting to access uninitialized memory
  Bluetooth: L2CAP: Fix l2cap_global_chan_by_psm
  Bluetooth: L2CAP: Fix accepting connection request for invalid SPSM
  Bluetooth: hci_conn: Fix not restoring ISO buffer count on disconnect
  Bluetooth: L2CAP: Fix memory leak in vhci_write
  Bluetooth: L2CAP: fix use-after-free in l2cap_conn_del()
  Bluetooth: virtio_bt: Use skb_put to set length
  Bluetooth: hci_conn: Fix CIS connection dst_type handling
  Bluetooth: L2CAP: Fix use-after-free caused by l2cap_reassemble_sdu
====================

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


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents ba9169f5 b1a2cd50
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -219,7 +219,7 @@ static void virtbt_rx_work(struct work_struct *work)
	if (!skb)
		return;

	skb->len = len;
	skb_put(skb, len);
	virtbt_rx_handle(vbt, skb);

	if (virtbt_add_inbuf(vbt) < 0)
+12 −6
Original line number Diff line number Diff line
@@ -1067,10 +1067,21 @@ int hci_conn_del(struct hci_conn *conn)
			hdev->acl_cnt += conn->sent;
	} else {
		struct hci_conn *acl = conn->link;

		if (acl) {
			acl->link = NULL;
			hci_conn_drop(acl);
		}

		/* Unacked ISO frames */
		if (conn->type == ISO_LINK) {
			if (hdev->iso_pkts)
				hdev->iso_cnt += conn->sent;
			else if (hdev->le_pkts)
				hdev->le_cnt += conn->sent;
			else
				hdev->acl_cnt += conn->sent;
		}
	}

	if (conn->amp_mgr)
@@ -1761,6 +1772,7 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
		if (!cis)
			return ERR_PTR(-ENOMEM);
		cis->cleanup = cis_cleanup;
		cis->dst_type = dst_type;
	}

	if (cis->state == BT_CONNECTED)
@@ -2140,12 +2152,6 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
	struct hci_conn *le;
	struct hci_conn *cis;

	/* Convert from ISO socket address type to HCI address type  */
	if (dst_type == BDADDR_LE_PUBLIC)
		dst_type = ADDR_LE_DEV_PUBLIC;
	else
		dst_type = ADDR_LE_DEV_RANDOM;

	if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
		le = hci_connect_le(hdev, dst, dst_type, false,
				    BT_SECURITY_LOW,
+12 −2
Original line number Diff line number Diff line
@@ -235,6 +235,14 @@ static int iso_chan_add(struct iso_conn *conn, struct sock *sk,
	return err;
}

static inline u8 le_addr_type(u8 bdaddr_type)
{
	if (bdaddr_type == BDADDR_LE_PUBLIC)
		return ADDR_LE_DEV_PUBLIC;
	else
		return ADDR_LE_DEV_RANDOM;
}

static int iso_connect_bis(struct sock *sk)
{
	struct iso_conn *conn;
@@ -328,14 +336,16 @@ static int iso_connect_cis(struct sock *sk)
	/* Just bind if DEFER_SETUP has been set */
	if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
		hcon = hci_bind_cis(hdev, &iso_pi(sk)->dst,
				    iso_pi(sk)->dst_type, &iso_pi(sk)->qos);
				    le_addr_type(iso_pi(sk)->dst_type),
				    &iso_pi(sk)->qos);
		if (IS_ERR(hcon)) {
			err = PTR_ERR(hcon);
			goto done;
		}
	} else {
		hcon = hci_connect_cis(hdev, &iso_pi(sk)->dst,
				       iso_pi(sk)->dst_type, &iso_pi(sk)->qos);
				       le_addr_type(iso_pi(sk)->dst_type),
				       &iso_pi(sk)->qos);
		if (IS_ERR(hcon)) {
			err = PTR_ERR(hcon);
			goto done;
+73 −13
Original line number Diff line number Diff line
@@ -1990,7 +1990,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
		if (link_type == LE_LINK && c->src_type == BDADDR_BREDR)
			continue;

		if (c->psm == psm) {
		if (c->chan_type != L2CAP_CHAN_FIXED && c->psm == psm) {
			int src_match, dst_match;
			int src_any, dst_any;

@@ -3764,7 +3764,8 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data
			l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
					   sizeof(rfc), (unsigned long) &rfc, endptr - ptr);

			if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
			if (remote_efs &&
			    test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
				chan->remote_id = efs.id;
				chan->remote_stype = efs.stype;
				chan->remote_msdu = le16_to_cpu(efs.msdu);
@@ -5813,6 +5814,19 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
	BT_DBG("psm 0x%2.2x scid 0x%4.4x mtu %u mps %u", __le16_to_cpu(psm),
	       scid, mtu, mps);

	/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 3, Part A
	 * page 1059:
	 *
	 * Valid range: 0x0001-0x00ff
	 *
	 * Table 4.15: L2CAP_LE_CREDIT_BASED_CONNECTION_REQ SPSM ranges
	 */
	if (!psm || __le16_to_cpu(psm) > L2CAP_PSM_LE_DYN_END) {
		result = L2CAP_CR_LE_BAD_PSM;
		chan = NULL;
		goto response;
	}

	/* Check if we have socket listening on psm */
	pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
					 &conn->hcon->dst, LE_LINK);
@@ -6001,6 +6015,18 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,

	psm  = req->psm;

	/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 3, Part A
	 * page 1059:
	 *
	 * Valid range: 0x0001-0x00ff
	 *
	 * Table 4.15: L2CAP_LE_CREDIT_BASED_CONNECTION_REQ SPSM ranges
	 */
	if (!psm || __le16_to_cpu(psm) > L2CAP_PSM_LE_DYN_END) {
		result = L2CAP_CR_LE_BAD_PSM;
		goto response;
	}

	BT_DBG("psm 0x%2.2x mtu %u mps %u", __le16_to_cpu(psm), mtu, mps);

	memset(&pdu, 0, sizeof(pdu));
@@ -6885,6 +6911,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
			       struct l2cap_ctrl *control,
			       struct sk_buff *skb, u8 event)
{
	struct l2cap_ctrl local_control;
	int err = 0;
	bool skb_in_use = false;

@@ -6909,15 +6936,32 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
			chan->buffer_seq = chan->expected_tx_seq;
			skb_in_use = true;

			/* l2cap_reassemble_sdu may free skb, hence invalidate
			 * control, so make a copy in advance to use it after
			 * l2cap_reassemble_sdu returns and to avoid the race
			 * condition, for example:
			 *
			 * The current thread calls:
			 *   l2cap_reassemble_sdu
			 *     chan->ops->recv == l2cap_sock_recv_cb
			 *       __sock_queue_rcv_skb
			 * Another thread calls:
			 *   bt_sock_recvmsg
			 *     skb_recv_datagram
			 *     skb_free_datagram
			 * Then the current thread tries to access control, but
			 * it was freed by skb_free_datagram.
			 */
			local_control = *control;
			err = l2cap_reassemble_sdu(chan, skb, control);
			if (err)
				break;

			if (control->final) {
			if (local_control.final) {
				if (!test_and_clear_bit(CONN_REJ_ACT,
							&chan->conn_state)) {
					control->final = 0;
					l2cap_retransmit_all(chan, control);
					local_control.final = 0;
					l2cap_retransmit_all(chan, &local_control);
					l2cap_ertm_send(chan);
				}
			}
@@ -7297,11 +7341,27 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
			   struct sk_buff *skb)
{
	/* l2cap_reassemble_sdu may free skb, hence invalidate control, so store
	 * the txseq field in advance to use it after l2cap_reassemble_sdu
	 * returns and to avoid the race condition, for example:
	 *
	 * The current thread calls:
	 *   l2cap_reassemble_sdu
	 *     chan->ops->recv == l2cap_sock_recv_cb
	 *       __sock_queue_rcv_skb
	 * Another thread calls:
	 *   bt_sock_recvmsg
	 *     skb_recv_datagram
	 *     skb_free_datagram
	 * Then the current thread tries to access control, but it was freed by
	 * skb_free_datagram.
	 */
	u16 txseq = control->txseq;

	BT_DBG("chan %p, control %p, skb %p, state %d", chan, control, skb,
	       chan->rx_state);

	if (l2cap_classify_txseq(chan, control->txseq) ==
	    L2CAP_TXSEQ_EXPECTED) {
	if (l2cap_classify_txseq(chan, txseq) == L2CAP_TXSEQ_EXPECTED) {
		l2cap_pass_to_tx(chan, control);

		BT_DBG("buffer_seq %u->%u", chan->buffer_seq,
@@ -7324,8 +7384,8 @@ static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
		}
	}

	chan->last_acked_seq = control->txseq;
	chan->expected_tx_seq = __next_seq(chan, control->txseq);
	chan->last_acked_seq = txseq;
	chan->expected_tx_seq = __next_seq(chan, txseq);

	return 0;
}
@@ -7581,6 +7641,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
				return;
			}

			l2cap_chan_hold(chan);
			l2cap_chan_lock(chan);
		} else {
			BT_DBG("unknown cid 0x%4.4x", cid);
@@ -8426,9 +8487,8 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
		 * expected length.
		 */
		if (skb->len < L2CAP_LEN_SIZE) {
			if (l2cap_recv_frag(conn, skb, conn->mtu) < 0)
				goto drop;
			return;
			l2cap_recv_frag(conn, skb, conn->mtu);
			break;
		}

		len = get_unaligned_le16(skb->data) + L2CAP_HDR_SIZE;
@@ -8472,7 +8532,7 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)

			/* Header still could not be read just continue */
			if (conn->rx_skb->len < L2CAP_LEN_SIZE)
				return;
				break;
		}

		if (skb->len > conn->rx_len) {