summaryrefslogtreecommitdiff
path: root/net/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/hci_conn.c4
-rw-r--r--net/bluetooth/hci_sync.c2
-rw-r--r--net/bluetooth/hidp/core.c16
-rw-r--r--net/bluetooth/l2cap_core.c51
-rw-r--r--net/bluetooth/mgmt.c7
-rw-r--r--net/bluetooth/smp.c2
6 files changed, 51 insertions, 31 deletions
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 4719dac07190..6eb59e9f2aa8 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1944,6 +1944,8 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
return false;
done:
+ conn->iso_qos = *qos;
+
if (hci_cmd_sync_queue(hdev, set_cig_params_sync,
UINT_PTR(qos->ucast.cig), NULL) < 0)
return false;
@@ -2013,8 +2015,6 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
}
hci_conn_hold(cis);
-
- cis->iso_qos = *qos;
cis->state = BT_BOUND;
return cis;
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 121dbc8208ec..3166914b0d6c 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -6627,8 +6627,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
* state.
*/
if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
- hci_scan_disable_sync(hdev);
hci_dev_set_flag(hdev, HCI_LE_SCAN_INTERRUPTED);
+ hci_scan_disable_sync(hdev);
}
/* Update random address, but set require_privacy to false so
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 6fe815241b01..7bcf8c5ceaee 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -986,7 +986,8 @@ static void session_free(struct kref *ref)
skb_queue_purge(&session->intr_transmit);
fput(session->intr_sock->file);
fput(session->ctrl_sock->file);
- l2cap_conn_put(session->conn);
+ if (session->conn)
+ l2cap_conn_put(session->conn);
kfree(session);
}
@@ -1164,6 +1165,15 @@ static void hidp_session_remove(struct l2cap_conn *conn,
down_write(&hidp_session_sem);
+ /* Drop L2CAP reference immediately to indicate that
+ * l2cap_unregister_user() shall not be called as it is already
+ * considered removed.
+ */
+ if (session->conn) {
+ l2cap_conn_put(session->conn);
+ session->conn = NULL;
+ }
+
hidp_session_terminate(session);
cancel_work_sync(&session->dev_init);
@@ -1301,7 +1311,9 @@ static int hidp_session_thread(void *arg)
* Instead, this call has the same semantics as if user-space tried to
* delete the session.
*/
- l2cap_unregister_user(session->conn, &session->user);
+ if (session->conn)
+ l2cap_unregister_user(session->conn, &session->user);
+
hidp_session_put(session);
module_put_and_kthread_exit(0);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ad98db9632fd..5deb6c4f1e41 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1678,17 +1678,15 @@ static void l2cap_info_timeout(struct work_struct *work)
int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user)
{
- struct hci_dev *hdev = conn->hcon->hdev;
int ret;
/* We need to check whether l2cap_conn is registered. If it is not, we
- * must not register the l2cap_user. l2cap_conn_del() is unregisters
- * l2cap_conn objects, but doesn't provide its own locking. Instead, it
- * relies on the parent hci_conn object to be locked. This itself relies
- * on the hci_dev object to be locked. So we must lock the hci device
- * here, too. */
+ * must not register the l2cap_user. l2cap_conn_del() unregisters
+ * l2cap_conn objects under conn->lock, and we use the same lock here
+ * to protect access to conn->users and conn->hchan.
+ */
- hci_dev_lock(hdev);
+ mutex_lock(&conn->lock);
if (!list_empty(&user->list)) {
ret = -EINVAL;
@@ -1709,16 +1707,14 @@ int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user)
ret = 0;
out_unlock:
- hci_dev_unlock(hdev);
+ mutex_unlock(&conn->lock);
return ret;
}
EXPORT_SYMBOL(l2cap_register_user);
void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)
{
- struct hci_dev *hdev = conn->hcon->hdev;
-
- hci_dev_lock(hdev);
+ mutex_lock(&conn->lock);
if (list_empty(&user->list))
goto out_unlock;
@@ -1727,7 +1723,7 @@ void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)
user->remove(conn, user);
out_unlock:
- hci_dev_unlock(hdev);
+ mutex_unlock(&conn->lock);
}
EXPORT_SYMBOL(l2cap_unregister_user);
@@ -4616,7 +4612,8 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
switch (type) {
case L2CAP_IT_FEAT_MASK:
- conn->feat_mask = get_unaligned_le32(rsp->data);
+ if (cmd_len >= sizeof(*rsp) + sizeof(u32))
+ conn->feat_mask = get_unaligned_le32(rsp->data);
if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) {
struct l2cap_info_req req;
@@ -4635,7 +4632,8 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
break;
case L2CAP_IT_FIXED_CHAN:
- conn->remote_fixed_chan = rsp->data[0];
+ if (cmd_len >= sizeof(*rsp) + sizeof(rsp->data[0]))
+ conn->remote_fixed_chan = rsp->data[0];
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
@@ -5059,7 +5057,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
u16 mtu, mps;
__le16 psm;
u8 result, rsp_len = 0;
- int i, num_scid;
+ int i, num_scid = 0;
bool defer = false;
if (!enable_ecred)
@@ -5072,6 +5070,14 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
goto response;
}
+ /* Check if there are no pending channels with the same ident */
+ __l2cap_chan_list_id(conn, cmd->ident, l2cap_ecred_list_defer,
+ &num_scid);
+ if (num_scid) {
+ result = L2CAP_CR_LE_INVALID_PARAMS;
+ goto response;
+ }
+
cmd_len -= sizeof(*req);
num_scid = cmd_len / sizeof(u16);
@@ -5424,7 +5430,7 @@ static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn,
u8 *data)
{
struct l2cap_chan *chan, *tmp;
- struct l2cap_ecred_conn_rsp *rsp = (void *) data;
+ struct l2cap_ecred_reconf_rsp *rsp = (void *)data;
u16 result;
if (cmd_len < sizeof(*rsp))
@@ -5432,7 +5438,7 @@ static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn,
result = __le16_to_cpu(rsp->result);
- BT_DBG("result 0x%4.4x", rsp->result);
+ BT_DBG("result 0x%4.4x", result);
if (!result)
return 0;
@@ -6662,8 +6668,10 @@ static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
return -ENOBUFS;
}
- if (chan->imtu < skb->len) {
- BT_ERR("Too big LE L2CAP PDU");
+ if (skb->len > chan->imtu) {
+ BT_ERR("Too big LE L2CAP PDU: len %u > %u", skb->len,
+ chan->imtu);
+ l2cap_send_disconn_req(chan, ECONNRESET);
return -ENOBUFS;
}
@@ -6689,7 +6697,9 @@ static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
sdu_len, skb->len, chan->imtu);
if (sdu_len > chan->imtu) {
- BT_ERR("Too big LE L2CAP SDU length received");
+ BT_ERR("Too big LE L2CAP SDU length: len %u > %u",
+ skb->len, sdu_len);
+ l2cap_send_disconn_req(chan, ECONNRESET);
err = -EMSGSIZE;
goto failed;
}
@@ -6725,6 +6735,7 @@ static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
if (chan->sdu->len + skb->len > chan->sdu_len) {
BT_ERR("Too much LE L2CAP data received");
+ l2cap_send_disconn_req(chan, ECONNRESET);
err = -EINVAL;
goto failed;
}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a7238fd3b03b..d52238ce6a9a 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2195,10 +2195,7 @@ static void set_mesh_complete(struct hci_dev *hdev, void *data, int err)
sk = cmd->sk;
if (status) {
- mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_MESH_RECEIVER,
- status);
- mgmt_pending_foreach(MGMT_OP_SET_MESH_RECEIVER, hdev, true,
- cmd_status_rsp, &status);
+ mgmt_cmd_status(cmd->sk, hdev->id, cmd->opcode, status);
goto done;
}
@@ -5377,7 +5374,7 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode,
mgmt_status(status), &rp, sizeof(rp));
- mgmt_pending_remove(cmd);
+ mgmt_pending_free(cmd);
hci_dev_unlock(hdev);
bt_dev_dbg(hdev, "add monitor %d complete, status %d",
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index e67bf7b34ea7..485e3468bd26 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -2743,7 +2743,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
if (!test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags) &&
!crypto_memneq(key, smp->local_pk, 64)) {
bt_dev_err(hdev, "Remote and local public keys are identical");
- return SMP_UNSPECIFIED;
+ return SMP_DHKEY_CHECK_FAILED;
}
memcpy(smp->remote_pk, key, 64);