Commit ef1fdc93 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge tag 'for-net-2022-10-02' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth

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.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents ba9169f5 b1a2cd50
...@@ -219,7 +219,7 @@ static void virtbt_rx_work(struct work_struct *work) ...@@ -219,7 +219,7 @@ static void virtbt_rx_work(struct work_struct *work)
if (!skb) if (!skb)
return; return;
skb->len = len; skb_put(skb, len);
virtbt_rx_handle(vbt, skb); virtbt_rx_handle(vbt, skb);
if (virtbt_add_inbuf(vbt) < 0) if (virtbt_add_inbuf(vbt) < 0)
......
...@@ -1067,10 +1067,21 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -1067,10 +1067,21 @@ int hci_conn_del(struct hci_conn *conn)
hdev->acl_cnt += conn->sent; hdev->acl_cnt += conn->sent;
} else { } else {
struct hci_conn *acl = conn->link; struct hci_conn *acl = conn->link;
if (acl) { if (acl) {
acl->link = NULL; acl->link = NULL;
hci_conn_drop(acl); 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) if (conn->amp_mgr)
...@@ -1761,6 +1772,7 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -1761,6 +1772,7 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
if (!cis) if (!cis)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
cis->cleanup = cis_cleanup; cis->cleanup = cis_cleanup;
cis->dst_type = dst_type;
} }
if (cis->state == BT_CONNECTED) if (cis->state == BT_CONNECTED)
...@@ -2140,12 +2152,6 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst, ...@@ -2140,12 +2152,6 @@ struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
struct hci_conn *le; struct hci_conn *le;
struct hci_conn *cis; 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)) if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
le = hci_connect_le(hdev, dst, dst_type, false, le = hci_connect_le(hdev, dst, dst_type, false,
BT_SECURITY_LOW, BT_SECURITY_LOW,
......
...@@ -235,6 +235,14 @@ static int iso_chan_add(struct iso_conn *conn, struct sock *sk, ...@@ -235,6 +235,14 @@ static int iso_chan_add(struct iso_conn *conn, struct sock *sk,
return err; 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) static int iso_connect_bis(struct sock *sk)
{ {
struct iso_conn *conn; struct iso_conn *conn;
...@@ -328,14 +336,16 @@ static int iso_connect_cis(struct sock *sk) ...@@ -328,14 +336,16 @@ static int iso_connect_cis(struct sock *sk)
/* Just bind if DEFER_SETUP has been set */ /* Just bind if DEFER_SETUP has been set */
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
hcon = hci_bind_cis(hdev, &iso_pi(sk)->dst, 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)) { if (IS_ERR(hcon)) {
err = PTR_ERR(hcon); err = PTR_ERR(hcon);
goto done; goto done;
} }
} else { } else {
hcon = hci_connect_cis(hdev, &iso_pi(sk)->dst, 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)) { if (IS_ERR(hcon)) {
err = PTR_ERR(hcon); err = PTR_ERR(hcon);
goto done; goto done;
......
...@@ -1990,7 +1990,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, ...@@ -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) if (link_type == LE_LINK && c->src_type == BDADDR_BREDR)
continue; continue;
if (c->psm == psm) { if (c->chan_type != L2CAP_CHAN_FIXED && c->psm == psm) {
int src_match, dst_match; int src_match, dst_match;
int src_any, dst_any; int src_any, dst_any;
...@@ -3764,7 +3764,8 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data ...@@ -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, l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc, endptr - ptr); 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_id = efs.id;
chan->remote_stype = efs.stype; chan->remote_stype = efs.stype;
chan->remote_msdu = le16_to_cpu(efs.msdu); chan->remote_msdu = le16_to_cpu(efs.msdu);
...@@ -5813,6 +5814,19 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, ...@@ -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), BT_DBG("psm 0x%2.2x scid 0x%4.4x mtu %u mps %u", __le16_to_cpu(psm),
scid, mtu, mps); 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 */ /* Check if we have socket listening on psm */
pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src, pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
&conn->hcon->dst, LE_LINK); &conn->hcon->dst, LE_LINK);
...@@ -6001,6 +6015,18 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, ...@@ -6001,6 +6015,18 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
psm = req->psm; 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); BT_DBG("psm 0x%2.2x mtu %u mps %u", __le16_to_cpu(psm), mtu, mps);
memset(&pdu, 0, sizeof(pdu)); memset(&pdu, 0, sizeof(pdu));
...@@ -6885,6 +6911,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan, ...@@ -6885,6 +6911,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
struct l2cap_ctrl *control, struct l2cap_ctrl *control,
struct sk_buff *skb, u8 event) struct sk_buff *skb, u8 event)
{ {
struct l2cap_ctrl local_control;
int err = 0; int err = 0;
bool skb_in_use = false; bool skb_in_use = false;
...@@ -6909,15 +6936,32 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan, ...@@ -6909,15 +6936,32 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
chan->buffer_seq = chan->expected_tx_seq; chan->buffer_seq = chan->expected_tx_seq;
skb_in_use = true; 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); err = l2cap_reassemble_sdu(chan, skb, control);
if (err) if (err)
break; break;
if (control->final) { if (local_control.final) {
if (!test_and_clear_bit(CONN_REJ_ACT, if (!test_and_clear_bit(CONN_REJ_ACT,
&chan->conn_state)) { &chan->conn_state)) {
control->final = 0; local_control.final = 0;
l2cap_retransmit_all(chan, control); l2cap_retransmit_all(chan, &local_control);
l2cap_ertm_send(chan); l2cap_ertm_send(chan);
} }
} }
...@@ -7297,11 +7341,27 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control, ...@@ -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, static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff *skb) 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, BT_DBG("chan %p, control %p, skb %p, state %d", chan, control, skb,
chan->rx_state); chan->rx_state);
if (l2cap_classify_txseq(chan, control->txseq) == if (l2cap_classify_txseq(chan, txseq) == L2CAP_TXSEQ_EXPECTED) {
L2CAP_TXSEQ_EXPECTED) {
l2cap_pass_to_tx(chan, control); l2cap_pass_to_tx(chan, control);
BT_DBG("buffer_seq %u->%u", chan->buffer_seq, 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, ...@@ -7324,8 +7384,8 @@ static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
} }
} }
chan->last_acked_seq = control->txseq; chan->last_acked_seq = txseq;
chan->expected_tx_seq = __next_seq(chan, control->txseq); chan->expected_tx_seq = __next_seq(chan, txseq);
return 0; return 0;
} }
...@@ -7581,6 +7641,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid, ...@@ -7581,6 +7641,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
return; return;
} }
l2cap_chan_hold(chan);
l2cap_chan_lock(chan); l2cap_chan_lock(chan);
} else { } else {
BT_DBG("unknown cid 0x%4.4x", cid); 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) ...@@ -8426,9 +8487,8 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
* expected length. * expected length.
*/ */
if (skb->len < L2CAP_LEN_SIZE) { if (skb->len < L2CAP_LEN_SIZE) {
if (l2cap_recv_frag(conn, skb, conn->mtu) < 0) l2cap_recv_frag(conn, skb, conn->mtu);
goto drop; break;
return;
} }
len = get_unaligned_le16(skb->data) + L2CAP_HDR_SIZE; 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) ...@@ -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 */ /* Header still could not be read just continue */
if (conn->rx_skb->len < L2CAP_LEN_SIZE) if (conn->rx_skb->len < L2CAP_LEN_SIZE)
return; break;
} }
if (skb->len > conn->rx_len) { if (skb->len > conn->rx_len) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment