summaryrefslogtreecommitdiff
path: root/net/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/hci_conn.c14
-rw-r--r--net/bluetooth/hci_sync.c216
-rw-r--r--net/bluetooth/hidp/core.c30
-rw-r--r--net/bluetooth/iso.c273
-rw-r--r--net/bluetooth/l2cap_core.c5
-rw-r--r--net/bluetooth/mgmt.c84
-rw-r--r--net/bluetooth/rfcomm/core.c5
-rw-r--r--net/bluetooth/sco.c22
8 files changed, 461 insertions, 188 deletions
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 1966cd153d97..b1f911fd4ad6 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1123,6 +1123,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle);
INIT_DELAYED_WORK(&conn->le_conn_timeout, le_conn_timeout);
+ spin_lock_init(&conn->proto_lock);
+
atomic_set(&conn->refcnt, 0);
hci_dev_hold(hdev);
@@ -3163,6 +3165,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
}
+static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err)
+{
+ struct hci_conn *conn = data;
+
+ hci_conn_put(conn);
+}
+
int hci_abort_conn(struct hci_conn *conn, u8 reason)
{
struct hci_dev *hdev = conn->hdev;
@@ -3188,7 +3197,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
* already queue its callback on cmd_sync_work.
*/
- err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
+ err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn),
+ abort_conn_destroy);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index c0b1fc293b49..c8d14128c363 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -1233,10 +1233,11 @@ static int hci_set_adv_set_random_addr_sync(struct hci_dev *hdev, u8 instance,
}
static int
-hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv,
+hci_set_ext_adv_params_sync(struct hci_dev *hdev, u8 instance,
const struct hci_cp_le_set_ext_adv_params *cp,
struct hci_rp_le_set_ext_adv_params *rp)
{
+ struct adv_info *adv;
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(*cp),
@@ -1264,11 +1265,15 @@ hci_set_ext_adv_params_sync(struct hci_dev *hdev, struct adv_info *adv,
if (!rp->status) {
hdev->adv_addr_type = cp->own_addr_type;
- if (!cp->handle) {
+ if (!instance) {
/* Store in hdev for instance 0 */
hdev->adv_tx_power = rp->tx_power;
- } else if (adv) {
- adv->tx_power = rp->tx_power;
+ } else {
+ hci_dev_lock(hdev);
+ adv = hci_find_adv_instance(hdev, instance);
+ if (adv)
+ adv->tx_power = rp->tx_power;
+ hci_dev_unlock(hdev);
}
}
@@ -1284,9 +1289,13 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
int err;
if (instance) {
+ hci_dev_lock(hdev);
+
adv = hci_find_adv_instance(hdev, instance);
- if (!adv || !adv->adv_data_changed)
+ if (!adv || !adv->adv_data_changed) {
+ hci_dev_unlock(hdev);
return 0;
+ }
}
len = eir_create_adv_data(hdev, instance, pdu->data,
@@ -1297,16 +1306,27 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
+ if (adv) {
+ adv->adv_data_changed = false;
+ hci_dev_unlock(hdev);
+ }
+
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA,
struct_size(pdu, data, len), pdu,
HCI_CMD_TIMEOUT);
- if (err)
+ if (err) {
+ if (instance) {
+ hci_dev_lock(hdev);
+ adv = hci_find_adv_instance(hdev, instance);
+ if (adv)
+ adv->adv_data_changed = true;
+ hci_dev_unlock(hdev);
+ }
+
return err;
+ }
- /* Update data if the command succeed */
- if (adv) {
- adv->adv_data_changed = false;
- } else {
+ if (!instance) {
memcpy(hdev->adv_data, pdu->data, len);
hdev->adv_data_len = len;
}
@@ -1360,22 +1380,22 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
struct adv_info *adv;
bool secondary_adv;
- if (instance > 0) {
- adv = hci_find_adv_instance(hdev, instance);
- if (!adv)
- return -EINVAL;
- } else {
- adv = NULL;
- }
-
/* Updating parameters of an active instance will return a
- * Command Disallowed error, so we must first disable the
- * instance if it is active.
+ * Command Disallowed error, so disable it before taking a snapshot.
*/
- if (adv) {
+ if (instance > 0) {
err = hci_disable_ext_adv_instance_sync(hdev, instance);
if (err)
return err;
+
+ hci_dev_lock(hdev);
+ adv = hci_find_adv_instance(hdev, instance);
+ if (!adv) {
+ hci_dev_unlock(hdev);
+ return -EINVAL;
+ }
+ } else {
+ adv = NULL;
}
flags = hci_adv_instance_flags(hdev, instance);
@@ -1386,8 +1406,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
mgmt_get_connectable(hdev);
- if (!is_advertising_allowed(hdev, connectable))
+ if (!is_advertising_allowed(hdev, connectable)) {
+ if (instance)
+ hci_dev_unlock(hdev);
return -EPERM;
+ }
/* Set require_privacy to true only when non-connectable
* advertising is used and it is not periodic.
@@ -1398,8 +1421,11 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
err = hci_get_random_address(hdev, require_privacy,
adv_use_rpa(hdev, flags), adv,
&own_addr_type, &random_addr);
- if (err < 0)
+ if (err < 0) {
+ if (instance)
+ hci_dev_unlock(hdev);
return err;
+ }
memset(&cp, 0, sizeof(cp));
@@ -1450,6 +1476,9 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
cp.channel_map = hdev->le_adv_channel_map;
cp.handle = adv ? adv->handle : instance;
+ if (instance)
+ hci_dev_unlock(hdev);
+
if (flags & MGMT_ADV_FLAG_SEC_2M) {
cp.primary_phy = HCI_ADV_PHY_1M;
cp.secondary_phy = HCI_ADV_PHY_2M;
@@ -1462,12 +1491,12 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
cp.secondary_phy = HCI_ADV_PHY_1M;
}
- err = hci_set_ext_adv_params_sync(hdev, adv, &cp, &rp);
+ err = hci_set_ext_adv_params_sync(hdev, instance, &cp, &rp);
if (err)
return err;
/* Update adv data as tx power is known now */
- err = hci_set_ext_adv_data_sync(hdev, cp.handle);
+ err = hci_set_ext_adv_data_sync(hdev, instance);
if (err)
return err;
@@ -1475,9 +1504,14 @@ int hci_setup_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance)
own_addr_type == ADDR_LE_DEV_RANDOM_RESOLVED) &&
bacmp(&random_addr, BDADDR_ANY)) {
/* Check if random address need to be updated */
- if (adv) {
- if (!bacmp(&random_addr, &adv->random_addr))
+ if (instance) {
+ hci_dev_lock(hdev);
+ adv = hci_find_adv_instance(hdev, instance);
+ if (!adv || !bacmp(&random_addr, &adv->random_addr)) {
+ hci_dev_unlock(hdev);
return 0;
+ }
+ hci_dev_unlock(hdev);
} else {
if (!bacmp(&random_addr, &hdev->random_addr))
return 0;
@@ -1499,9 +1533,13 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
int err;
if (instance) {
+ hci_dev_lock(hdev);
+
adv = hci_find_adv_instance(hdev, instance);
- if (!adv || !adv->scan_rsp_changed)
+ if (!adv || !adv->scan_rsp_changed) {
+ hci_dev_unlock(hdev);
return 0;
+ }
}
len = eir_create_scan_rsp(hdev, instance, pdu->data);
@@ -1511,15 +1549,27 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
+ if (adv) {
+ adv->scan_rsp_changed = false;
+ hci_dev_unlock(hdev);
+ }
+
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_RSP_DATA,
struct_size(pdu, data, len), pdu,
HCI_CMD_TIMEOUT);
- if (err)
+ if (err) {
+ if (instance) {
+ hci_dev_lock(hdev);
+ adv = hci_find_adv_instance(hdev, instance);
+ if (adv)
+ adv->scan_rsp_changed = true;
+ hci_dev_unlock(hdev);
+ }
+
return err;
+ }
- if (adv) {
- adv->scan_rsp_changed = false;
- } else {
+ if (!instance) {
memcpy(hdev->scan_rsp_data, pdu->data, len);
hdev->scan_rsp_data_len = len;
}
@@ -1534,8 +1584,14 @@ static int __hci_set_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
memset(&cp, 0, sizeof(cp));
+ if (instance)
+ hci_dev_lock(hdev);
+
len = eir_create_scan_rsp(hdev, instance, cp.data);
+ if (instance)
+ hci_dev_unlock(hdev);
+
if (hdev->scan_rsp_data_len == len &&
!memcmp(cp.data, hdev->scan_rsp_data, len))
return 0;
@@ -1670,9 +1726,13 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance)
struct adv_info *adv = NULL;
if (instance) {
+ hci_dev_lock(hdev);
+
adv = hci_find_adv_instance(hdev, instance);
- if (!adv || !adv->periodic)
+ if (!adv || !adv->periodic) {
+ hci_dev_unlock(hdev);
return 0;
+ }
}
len = eir_create_per_adv_data(hdev, instance, pdu->data);
@@ -1681,6 +1741,9 @@ static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance)
pdu->handle = adv ? adv->handle : instance;
pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
+ if (adv)
+ hci_dev_unlock(hdev);
+
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA,
struct_size(pdu, data, len), pdu,
HCI_CMD_TIMEOUT);
@@ -6523,7 +6586,7 @@ static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev,
if (err)
return err;
- err = hci_set_ext_adv_params_sync(hdev, NULL, &cp, &rp);
+ err = hci_set_ext_adv_params_sync(hdev, 0, &cp, &rp);
if (err)
return err;
@@ -6678,11 +6741,6 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
bt_dev_dbg(hdev, "conn %p", conn);
- /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
- * clear_bit() at done.
- */
- hci_conn_get(conn);
-
clear_bit(HCI_CONN_SCANNING, &conn->flags);
conn->state = BT_CONNECT;
@@ -6694,8 +6752,9 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
hdev->le_scan_type == LE_SCAN_ACTIVE &&
!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
- hci_conn_del(conn);
- hci_conn_put(conn);
+ conn->state = BT_OPEN;
+ hci_abort_conn_sync(hdev, conn,
+ HCI_ERROR_REJ_LIMITED_RESOURCES);
return -EBUSY;
}
@@ -6793,7 +6852,6 @@ done:
/* Re-enable advertising after the connection attempt is finished. */
hci_resume_advertising_sync(hdev);
- hci_conn_put(conn);
return err;
}
@@ -7068,11 +7126,6 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
else
cp.role_switch = 0x00;
- /* Hold a reference so conn stays valid for the HCI_CONN_CREATE
- * clear_bit() below.
- */
- hci_conn_get(conn);
-
/* Mark create connection in flight so hci_cancel_connect_sync() can
* cancel it while blocking on the connection complete event.
*/
@@ -7084,17 +7137,27 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
conn->conn_timeout, NULL);
clear_bit(HCI_CONN_CREATE, &conn->flags);
- hci_conn_put(conn);
return err;
}
+static void hci_acl_create_conn_sync_complete(struct hci_dev *hdev, void *data,
+ int err)
+{
+ struct hci_conn *conn = data;
+
+ hci_conn_put(conn);
+}
+
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
- NULL);
+ err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync,
+ hci_conn_get(conn),
+ hci_acl_create_conn_sync_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
@@ -7105,36 +7168,41 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
bt_dev_dbg(hdev, "err %d", err);
if (err == -ECANCELED)
- return;
+ goto done;
hci_dev_lock(hdev);
if (!hci_conn_valid(hdev, conn))
- goto done;
+ goto unlock;
if (!err) {
hci_connect_le_scan_cleanup(conn, 0x00);
- goto done;
+ goto unlock;
}
/* Check if connection is still pending */
if (conn != hci_lookup_le_connect(hdev))
- goto done;
+ goto unlock;
/* Flush to make sure we send create conn cancel command if needed */
flush_delayed_work(&conn->le_conn_timeout);
hci_conn_failed(conn, bt_status(err));
-done:
+unlock:
hci_dev_unlock(hdev);
+done:
+ hci_conn_put(conn);
}
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
+ err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync,
+ hci_conn_get(conn),
create_le_conn_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
@@ -7257,7 +7325,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
bt_dev_dbg(hdev, "err %d", err);
if (err == -ECANCELED)
- return;
+ goto done;
hci_dev_lock(hdev);
@@ -7281,6 +7349,8 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
unlock:
hci_dev_unlock(hdev);
+done:
+ hci_conn_put(conn);
}
static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn,
@@ -7431,8 +7501,11 @@ int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
+ err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync,
+ hci_conn_get(conn),
create_pa_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
@@ -7443,10 +7516,12 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
bt_dev_dbg(hdev, "err %d", err);
if (err == -ECANCELED)
- return;
+ goto done;
- if (hci_conn_valid(hdev, conn))
- clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
+ clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
+
+done:
+ hci_conn_put(conn);
}
static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
@@ -7498,8 +7573,14 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
+ if (!conn)
+ return 0;
+
+ err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync,
+ hci_conn_get(conn),
create_big_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
@@ -7514,6 +7595,8 @@ static void past_complete(struct hci_dev *hdev, void *data, int err)
bt_dev_dbg(hdev, "err %d", err);
+ hci_conn_put(past->conn);
+ hci_conn_put(past->le);
kfree(past);
}
@@ -7578,8 +7661,8 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
if (!data)
return -ENOMEM;
- data->conn = conn;
- data->le = le;
+ data->conn = hci_conn_get(conn);
+ data->le = hci_conn_get(le);
if (conn->role == HCI_ROLE_MASTER)
err = hci_cmd_sync_queue_once(conn->hdev,
@@ -7589,8 +7672,11 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
err = hci_cmd_sync_queue_once(conn->hdev, hci_le_past_sync,
data, past_complete);
- if (err)
+ if (err) {
+ hci_conn_put(data->conn);
+ hci_conn_put(data->le);
kfree(data);
+ }
return (err == -EEXIST) ? 0 : err;
}
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 0e24c5e2955e..f5bdf9f1ca63 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -543,9 +543,10 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
}
if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) &&
- param == session->waiting_report_type) {
+ param == session->waiting_report_type) {
if (session->waiting_report_number < 0 ||
- session->waiting_report_number == skb->data[0]) {
+ (skb->len &&
+ session->waiting_report_number == skb->data[0])) {
/* hidp_get_raw_report() is waiting on this report. */
session->report_return = skb;
done_with_skb = 0;
@@ -560,16 +561,18 @@ static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb,
static void hidp_recv_ctrl_frame(struct hidp_session *session,
struct sk_buff *skb)
{
- unsigned char hdr, type, param;
+ unsigned char type, param;
+ u8 *hdr;
int free_skb = 1;
BT_DBG("session %p skb %p len %u", session, skb, skb->len);
- hdr = skb->data[0];
- skb_pull(skb, 1);
+ hdr = skb_pull_data(skb, 1);
+ if (!hdr)
+ goto free;
- type = hdr & HIDP_HEADER_TRANS_MASK;
- param = hdr & HIDP_HEADER_PARAM_MASK;
+ type = *hdr & HIDP_HEADER_TRANS_MASK;
+ param = *hdr & HIDP_HEADER_PARAM_MASK;
switch (type) {
case HIDP_TRANS_HANDSHAKE:
@@ -590,6 +593,7 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session,
break;
}
+free:
if (free_skb)
kfree_skb(skb);
}
@@ -597,14 +601,15 @@ static void hidp_recv_ctrl_frame(struct hidp_session *session,
static void hidp_recv_intr_frame(struct hidp_session *session,
struct sk_buff *skb)
{
- unsigned char hdr;
+ u8 *hdr;
BT_DBG("session %p skb %p len %u", session, skb, skb->len);
- hdr = skb->data[0];
- skb_pull(skb, 1);
+ hdr = skb_pull_data(skb, 1);
+ if (!hdr)
+ goto free;
- if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
+ if (*hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
hidp_set_timer(session);
if (session->input)
@@ -616,9 +621,10 @@ static void hidp_recv_intr_frame(struct hidp_session *session,
BT_DBG("report len %d", skb->len);
}
} else {
- BT_DBG("Unsupported protocol header 0x%02x", hdr);
+ BT_DBG("Unsupported protocol header 0x%02x", *hdr);
}
+free:
kfree_skb(skb);
}
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 2e95a153912c..a461c8a4efed 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -24,15 +24,19 @@ static struct bt_sock_list iso_sk_list = {
};
/* ---- ISO connections ---- */
+enum {
+ ISO_CONN_DROPPED,
+ __ISO_CONN_NUM_FLAGS
+};
+
struct iso_conn {
struct hci_conn *hcon;
+ DECLARE_BITMAP(flags, __ISO_CONN_NUM_FLAGS);
/* @lock: spinlock protecting changes to iso_conn fields */
spinlock_t lock;
struct sock *sk;
- struct delayed_work timeout_work;
-
struct sk_buff *rx_skb;
__u32 rx_len;
__u16 tx_sn;
@@ -56,6 +60,7 @@ static void iso_sock_kill(struct sock *sk);
enum {
BT_SK_BIG_SYNC,
BT_SK_PA_SYNC,
+ BT_SK_KILLED,
};
struct iso_pinfo {
@@ -74,6 +79,7 @@ struct iso_pinfo {
__u8 base_len;
__u8 base[BASE_MAX_LENGTH];
struct iso_conn *conn;
+ struct delayed_work timeout_work;
};
static struct bt_iso_qos default_qos;
@@ -102,16 +108,18 @@ static void iso_conn_free(struct kref *ref)
BT_DBG("conn %p", conn);
- if (conn->sk)
- iso_pi(conn->sk)->conn = NULL;
-
if (conn->hcon) {
- conn->hcon->iso_data = NULL;
- hci_conn_drop(conn->hcon);
- }
+ spin_lock(&conn->hcon->proto_lock);
- /* Ensure no more work items will run since hci_conn has been dropped */
- disable_delayed_work_sync(&conn->timeout_work);
+ /* Check we are not racing with iso_conn_add */
+ if (conn->hcon->iso_data == conn) {
+ conn->hcon->iso_data = NULL;
+ if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags))
+ hci_conn_drop(conn->hcon);
+ }
+
+ spin_unlock(&conn->hcon->proto_lock);
+ }
kfree_skb(conn->rx_skb);
@@ -125,7 +133,21 @@ static void iso_conn_put(struct iso_conn *conn)
BT_DBG("conn %p refcnt %d", conn, kref_read(&conn->ref));
+ /* The following race vs. iso_conn_del() is possible:
+ *
+ * 1. conn->hcon != NULL here
+ * 2. kref_put puts the last reference
+ * 3. concurrent iso_conn_del() gets iso_conn_hold_unless_zero() -> NULL
+ * and returns immediately, so conn->hcon is not cleared
+ * 4. iso_conn_free() dereferences conn->hcon
+ *
+ * To avoid UAF in step 4, take RCU before decrementing the refcount.
+ */
+ rcu_read_lock();
+
kref_put(&conn->ref, iso_conn_free);
+
+ rcu_read_unlock();
}
static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn)
@@ -141,6 +163,14 @@ static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn)
return conn;
}
+static struct iso_conn *iso_conn_hold(struct iso_conn *conn)
+{
+ BT_DBG("conn %p refcnt %u", conn, kref_read(&conn->ref));
+
+ kref_get(&conn->ref);
+ return conn;
+}
+
static struct sock *iso_sock_hold(struct iso_conn *conn)
{
if (!conn || !bt_sock_linked(&iso_sk_list, conn->sk))
@@ -153,78 +183,81 @@ static struct sock *iso_sock_hold(struct iso_conn *conn)
static void iso_sock_timeout(struct work_struct *work)
{
- struct iso_conn *conn = container_of(work, struct iso_conn,
- timeout_work.work);
- struct sock *sk;
-
- conn = iso_conn_hold_unless_zero(conn);
- if (!conn)
- return;
-
- iso_conn_lock(conn);
- sk = iso_sock_hold(conn);
- iso_conn_unlock(conn);
- iso_conn_put(conn);
-
- if (!sk)
- return;
+ struct iso_pinfo *pi = container_of(work, struct iso_pinfo,
+ timeout_work.work);
+ struct sock *sk = &pi->bt.sk;
BT_DBG("sock %p state %d", sk, sk->sk_state);
lock_sock(sk);
- sk->sk_err = ETIMEDOUT;
- sk->sk_state_change(sk);
+ if (!sock_flag(sk, SOCK_ZAPPED)) {
+ sk->sk_err = ETIMEDOUT;
+ sk->sk_state_change(sk);
+ }
release_sock(sk);
- sock_put(sk);
}
static void iso_sock_set_timer(struct sock *sk, long timeout)
{
+ lockdep_assert(lockdep_sock_is_held(sk));
+
+ cancel_delayed_work(&iso_pi(sk)->timeout_work);
+
if (!iso_pi(sk)->conn)
return;
BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout);
- cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
- schedule_delayed_work(&iso_pi(sk)->conn->timeout_work, timeout);
+ schedule_delayed_work(&iso_pi(sk)->timeout_work, timeout);
}
static void iso_sock_clear_timer(struct sock *sk)
{
- if (!iso_pi(sk)->conn)
- return;
+ BT_DBG("sock %p state %d", sk, sk->sk_state);
+ cancel_delayed_work(&iso_pi(sk)->timeout_work);
+}
+
+static void iso_sock_disable_timer(struct sock *sk)
+{
+ lockdep_assert(!lockdep_sock_is_held(sk));
BT_DBG("sock %p state %d", sk, sk->sk_state);
- cancel_delayed_work(&iso_pi(sk)->conn->timeout_work);
+ disable_delayed_work_sync(&iso_pi(sk)->timeout_work);
}
/* ---- ISO connections ---- */
static struct iso_conn *iso_conn_add(struct hci_conn *hcon)
+ __must_hold(&hcon->hdev->lock)
{
- struct iso_conn *conn = hcon->iso_data;
+ struct iso_conn *conn;
- conn = iso_conn_hold_unless_zero(conn);
+ spin_lock(&hcon->proto_lock);
+
+ conn = iso_conn_hold_unless_zero(hcon->iso_data);
if (conn) {
if (!conn->hcon) {
iso_conn_lock(conn);
conn->hcon = hcon;
iso_conn_unlock(conn);
}
- iso_conn_put(conn);
+ spin_unlock(&hcon->proto_lock);
return conn;
}
- conn = kzalloc_obj(*conn);
- if (!conn)
+ conn = kzalloc_obj(*conn, GFP_ATOMIC);
+ if (!conn) {
+ spin_unlock(&hcon->proto_lock);
return NULL;
+ }
kref_init(&conn->ref);
spin_lock_init(&conn->lock);
- INIT_DELAYED_WORK(&conn->timeout_work, iso_sock_timeout);
hcon->iso_data = conn;
conn->hcon = hcon;
conn->tx_sn = 0;
+ spin_unlock(&hcon->proto_lock);
+
BT_DBG("hcon %p conn %p", hcon, conn);
return conn;
@@ -263,11 +296,14 @@ static void iso_chan_del(struct sock *sk, int err)
}
static void iso_conn_del(struct hci_conn *hcon, int err)
+ __must_hold(&hcon->hdev->lock)
{
- struct iso_conn *conn = hcon->iso_data;
+ struct iso_conn *conn;
struct sock *sk;
- conn = iso_conn_hold_unless_zero(conn);
+ spin_lock(&hcon->proto_lock);
+ conn = iso_conn_hold_unless_zero(hcon->iso_data);
+ spin_unlock(&hcon->proto_lock);
if (!conn)
return;
@@ -277,18 +313,28 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
iso_conn_lock(conn);
sk = iso_sock_hold(conn);
iso_conn_unlock(conn);
- iso_conn_put(conn);
- if (!sk) {
- iso_conn_put(conn);
- return;
- }
+ if (!sk)
+ goto done;
+
+ iso_sock_disable_timer(sk);
lock_sock(sk);
- iso_sock_clear_timer(sk);
iso_chan_del(sk, err);
release_sock(sk);
+ iso_sock_kill(sk);
sock_put(sk);
+
+done:
+ /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */
+ spin_lock(&hcon->proto_lock);
+ iso_conn_lock(conn);
+ conn->hcon = NULL;
+ hcon->iso_data = NULL;
+ iso_conn_unlock(conn);
+ spin_unlock(&hcon->proto_lock);
+
+ iso_conn_put(conn);
}
static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
@@ -304,8 +350,14 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
return -EBUSY;
}
- iso_pi(sk)->conn = conn;
+ if (!conn->hcon) {
+ BT_ERR("conn->hcon missing");
+ return -EIO;
+ }
+
+ iso_pi(sk)->conn = iso_conn_hold(conn);
conn->sk = sk;
+ clear_bit(ISO_CONN_DROPPED, conn->flags);
if (parent)
bt_accept_enqueue(parent, sk, true);
@@ -402,6 +454,8 @@ static int iso_connect_bis(struct sock *sk)
iso_pi(sk)->bc_sid = hcon->sid;
}
+ lockdep_assert_held(&hcon->hdev->lock);
+
conn = iso_conn_add(hcon);
if (!conn) {
hci_conn_drop(hcon);
@@ -410,6 +464,7 @@ static int iso_connect_bis(struct sock *sk)
}
err = iso_chan_add(conn, sk, NULL);
+ iso_conn_put(conn);
if (err)
goto unlock;
@@ -504,6 +559,8 @@ static int iso_connect_cis(struct sock *sk)
}
}
+ lockdep_assert_held(&hcon->hdev->lock);
+
conn = iso_conn_add(hcon);
if (!conn) {
hci_conn_drop(hcon);
@@ -512,6 +569,7 @@ static int iso_connect_cis(struct sock *sk)
}
err = iso_chan_add(conn, sk, NULL);
+ iso_conn_put(conn);
if (err)
goto unlock;
@@ -790,9 +848,15 @@ static void iso_sock_cleanup_listen(struct sock *parent)
*/
static void iso_sock_kill(struct sock *sk)
{
+ iso_sock_disable_timer(sk);
+
+ lock_sock(sk);
+
if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket ||
- sock_flag(sk, SOCK_DEAD))
+ test_bit(BT_SK_KILLED, &iso_pi(sk)->flags)) {
+ release_sock(sk);
return;
+ }
BT_DBG("sk %p state %d", sk, sk->sk_state);
@@ -806,6 +870,9 @@ static void iso_sock_kill(struct sock *sk)
/* Kill poor orphan */
bt_sock_unlink(&iso_sk_list, sk);
sock_set_flag(sk, SOCK_DEAD);
+ set_bit(BT_SK_KILLED, &iso_pi(sk)->flags);
+
+ release_sock(sk);
sock_put(sk);
}
@@ -825,8 +892,8 @@ static void iso_sock_disconn(struct sock *sk)
*/
if (bis_sk) {
hcon->state = BT_OPEN;
- hcon->iso_data = NULL;
- iso_pi(sk)->conn->hcon = NULL;
+ set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags);
+
iso_sock_clear_timer(sk);
iso_chan_del(sk, bt_to_errno(hcon->abort_reason));
sock_put(bis_sk);
@@ -835,10 +902,8 @@ static void iso_sock_disconn(struct sock *sk)
}
sk->sk_state = BT_DISCONN;
- iso_conn_lock(iso_pi(sk)->conn);
- hci_conn_drop(iso_pi(sk)->conn->hcon);
- iso_pi(sk)->conn->hcon = NULL;
- iso_conn_unlock(iso_pi(sk)->conn);
+ if (!test_and_set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags))
+ hci_conn_drop(iso_pi(sk)->conn->hcon);
}
static void __iso_sock_close(struct sock *sk)
@@ -880,11 +945,11 @@ static void __iso_sock_close(struct sock *sk)
/* Must be called on unlocked socket. */
static void iso_sock_close(struct sock *sk)
{
+ iso_sock_disable_timer(sk);
+
lock_sock(sk);
- iso_sock_clear_timer(sk);
__iso_sock_close(sk);
release_sock(sk);
- iso_sock_kill(sk);
}
static void iso_sock_init(struct sock *sk, struct sock *parent)
@@ -951,6 +1016,8 @@ static struct sock *iso_sock_alloc(struct net *net, struct socket *sock,
iso_pi(sk)->qos = default_qos;
iso_pi(sk)->sync_handle = -1;
+ INIT_DELAYED_WORK(&iso_pi(sk)->timeout_work, iso_sock_timeout);
+
bt_sock_link(&iso_sk_list, sk);
return sk;
}
@@ -1033,15 +1100,15 @@ static int iso_sock_rebind_bis(struct sock *sk, struct sockaddr_iso *sa,
goto done;
}
- iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
-
- for (int i = 0; i < iso_pi(sk)->bc_num_bis; i++)
+ for (int i = 0; i < sa->iso_bc->bc_num_bis; i++)
if (sa->iso_bc->bc_bis[i] < 0x01 ||
sa->iso_bc->bc_bis[i] > 0x1f) {
err = -EINVAL;
goto done;
}
+ iso_pi(sk)->bc_num_bis = sa->iso_bc->bc_num_bis;
+
memcpy(iso_pi(sk)->bc_bis, sa->iso_bc->bc_bis,
iso_pi(sk)->bc_num_bis);
@@ -1276,6 +1343,8 @@ static int iso_listen_bis(struct sock *sk)
goto unlock;
}
+ lockdep_assert_held(&hcon->hdev->lock);
+
conn = iso_conn_add(hcon);
if (!conn) {
hci_conn_drop(hcon);
@@ -1284,10 +1353,9 @@ static int iso_listen_bis(struct sock *sk)
}
err = iso_chan_add(conn, sk, NULL);
- if (err) {
- hci_conn_drop(hcon);
+ iso_conn_put(conn);
+ if (err)
goto unlock;
- }
unlock:
release_sock(sk);
@@ -1466,6 +1534,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
BT_DBG("sock %p, sk %p", sock, sk);
+ lock_sock(sk);
+
addr->sa_family = AF_BLUETOOTH;
if (peer) {
@@ -1487,6 +1557,8 @@ static int iso_sock_getname(struct socket *sock, struct sockaddr *addr,
sa->iso_bdaddr_type = iso_pi(sk)->src_type;
}
+ release_sock(sk);
+
return len;
}
@@ -1786,7 +1858,7 @@ static bool check_bcast_qos(struct bt_iso_qos *qos)
return false;
if (!qos->bcast.timeout)
- qos->bcast.sync_timeout = BT_ISO_SYNC_TIMEOUT;
+ qos->bcast.timeout = BT_ISO_SYNC_TIMEOUT;
if (qos->bcast.timeout < 0x000a || qos->bcast.timeout > 0x4000)
return false;
@@ -2028,8 +2100,16 @@ static int iso_sock_release(struct socket *sock)
release_sock(sk);
}
+ /* Make sure sk is valid even if iso_conn_del() is concurrent */
+ sock_hold(sk);
+
+ lock_sock(sk);
sock_orphan(sk);
+ release_sock(sk);
+
iso_sock_kill(sk);
+
+ sock_put(sk);
return err;
}
@@ -2037,14 +2117,17 @@ static void iso_sock_ready(struct sock *sk)
{
BT_DBG("sk %p", sk);
- if (!sk)
+ lockdep_assert(lockdep_sock_is_held(sk));
+
+ switch (sk->sk_state) {
+ case BT_DISCONN:
+ case BT_CLOSED:
return;
+ }
- lock_sock(sk);
iso_sock_clear_timer(sk);
sk->sk_state = BT_CONNECTED;
sk->sk_state_change(sk);
- release_sock(sk);
}
static bool iso_match_big(struct sock *sk, void *data)
@@ -2074,7 +2157,7 @@ static bool iso_match_dst(struct sock *sk, void *data)
static void iso_conn_ready(struct iso_conn *conn)
{
struct sock *parent = NULL;
- struct sock *sk = conn->sk;
+ struct sock *sk;
struct hci_ev_le_big_sync_established *ev = NULL;
struct hci_ev_le_pa_sync_established *ev2 = NULL;
struct hci_ev_le_per_adv_report *ev3 = NULL;
@@ -2083,7 +2166,22 @@ static void iso_conn_ready(struct iso_conn *conn)
BT_DBG("conn %p", conn);
+ iso_conn_lock(conn);
+ sk = iso_sock_hold(conn);
+ iso_conn_unlock(conn);
+
if (sk) {
+ lock_sock(sk);
+
+ /* conn->sk may have become NULL if racing with sk close, but
+ * due to held hdev->lock, it can't become different sk.
+ */
+ if (!conn->sk) {
+ release_sock(sk);
+ sock_put(sk);
+ return;
+ }
+
/* Attempt to update source address in case of BIS Sender if
* the advertisement is using a random address.
*/
@@ -2096,14 +2194,15 @@ static void iso_conn_ready(struct iso_conn *conn)
adv = hci_find_adv_instance(bis->hdev,
bis->iso_qos.bcast.bis);
if (adv && bacmp(&adv->random_addr, BDADDR_ANY)) {
- lock_sock(sk);
iso_pi(sk)->src_type = BDADDR_LE_RANDOM;
bacpy(&iso_pi(sk)->src, &adv->random_addr);
- release_sock(sk);
}
}
- iso_sock_ready(conn->sk);
+ iso_sock_ready(sk);
+
+ release_sock(sk);
+ sock_put(sk);
} else {
hcon = conn->hcon;
if (!hcon)
@@ -2350,7 +2449,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
lock_sock(sk);
- hcon = iso_pi(sk)->conn->hcon;
+ hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL;
iso_pi(sk)->qos.bcast.encryption = ev2->encryption;
if (ev2->num_bis < iso_pi(sk)->bc_num_bis)
@@ -2390,9 +2489,11 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
if (!sk)
goto done;
- hcon = iso_pi(sk)->conn->hcon;
+ lock_sock(sk);
+
+ hcon = iso_pi(sk)->conn ? iso_pi(sk)->conn->hcon : NULL;
if (!hcon)
- goto done;
+ goto release3;
if (ev3->data_status == LE_PA_DATA_TRUNCATED) {
/* The controller was unable to retrieve PA data. */
@@ -2400,12 +2501,12 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
HCI_MAX_PER_AD_TOT_LEN);
hcon->le_per_adv_data_len = 0;
hcon->le_per_adv_data_offset = 0;
- goto done;
+ goto release3;
}
if (hcon->le_per_adv_data_offset + ev3->length >
HCI_MAX_PER_AD_TOT_LEN)
- goto done;
+ goto release3;
memcpy(hcon->le_per_adv_data + hcon->le_per_adv_data_offset,
ev3->data, ev3->length);
@@ -2424,18 +2525,19 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
&base_len);
if (!base || base_len > BASE_MAX_LENGTH)
- goto done;
+ goto release3;
- lock_sock(sk);
memcpy(iso_pi(sk)->base, base, base_len);
iso_pi(sk)->base_len = base_len;
- release_sock(sk);
} else {
/* This is a PA data fragment. Keep pa_data_len set to 0
* until all data has been reassembled.
*/
hcon->le_per_adv_data_len = 0;
}
+
+release3:
+ release_sock(sk);
} else {
sk = iso_get_sock(hdev, &hdev->bdaddr, BDADDR_ANY,
BT_LISTEN, iso_match_dst, BDADDR_ANY);
@@ -2454,6 +2556,7 @@ done:
}
static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
+ __must_hold(&hcon->hdev->lock)
{
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
hcon->type != PA_LINK) {
@@ -2465,8 +2568,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
struct hci_link *link, *t;
list_for_each_entry_safe(link, t, &hcon->link_list,
- list)
+ list) {
+ lockdep_assert_held(&link->conn->hdev->lock);
iso_conn_del(link->conn, bt_to_errno(status));
+ }
return;
}
@@ -2488,14 +2593,17 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
struct iso_conn *conn;
conn = iso_conn_add(hcon);
- if (conn)
+ if (conn) {
iso_conn_ready(conn);
+ iso_conn_put(conn);
+ }
} else {
iso_conn_del(hcon, bt_to_errno(status));
}
}
static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason)
+ __must_hold(&hcon->hdev->lock)
{
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
hcon->type != PA_LINK)
@@ -2522,7 +2630,10 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
return -ENOENT;
}
+ spin_lock(&hcon->proto_lock);
conn = iso_conn_hold_unless_zero(hcon->iso_data);
+ spin_unlock(&hcon->proto_lock);
+
hcon = NULL;
hci_dev_unlock(hdev);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 538ae9aa3479..1156aba4e83c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -4820,6 +4820,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
if (!chan)
return -EBADSLT;
+ chan = l2cap_chan_hold_unless_zero(chan);
+ if (!chan)
+ return -EBADSLT;
+
err = 0;
l2cap_chan_lock(chan);
@@ -4865,6 +4869,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
}
l2cap_chan_unlock(chan);
+ l2cap_chan_put(chan);
return err;
}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 1db10e0f617f..167d75e34526 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2696,18 +2696,28 @@ static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev,
static bool pending_eir_or_class(struct hci_dev *hdev)
{
struct mgmt_pending_cmd *cmd;
+ bool pending = false;
+
+ mutex_lock(&hdev->mgmt_pending_lock);
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
switch (cmd->opcode) {
case MGMT_OP_ADD_UUID:
case MGMT_OP_REMOVE_UUID:
case MGMT_OP_SET_DEV_CLASS:
+ case MGMT_OP_SET_LOCAL_NAME:
case MGMT_OP_SET_POWERED:
- return true;
+ pending = true;
+ break;
}
+
+ if (pending)
+ break;
}
- return false;
+ mutex_unlock(&hdev->mgmt_pending_lock);
+
+ return pending;
}
static const u8 bluetooth_base_uuid[] = {
@@ -3514,11 +3524,13 @@ static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data,
NULL, 0);
}
-static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn)
+static struct mgmt_pending_cmd *remove_pairing(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
struct mgmt_pending_cmd *cmd;
+ mutex_lock(&hdev->mgmt_pending_lock);
+
list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
continue;
@@ -3526,9 +3538,39 @@ static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn)
if (cmd->user_data != conn)
continue;
+ list_del(&cmd->list);
+ mutex_unlock(&hdev->mgmt_pending_lock);
return cmd;
}
+ mutex_unlock(&hdev->mgmt_pending_lock);
+
+ return NULL;
+}
+
+static struct mgmt_pending_cmd *remove_pairing_by_addr(struct hci_dev *hdev,
+ bdaddr_t *bdaddr)
+{
+ struct mgmt_pending_cmd *cmd;
+ struct hci_conn *conn;
+
+ mutex_lock(&hdev->mgmt_pending_lock);
+
+ list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+ if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
+ continue;
+
+ conn = cmd->user_data;
+ if (bacmp(bdaddr, &conn->dst) != 0)
+ continue;
+
+ list_del(&cmd->list);
+ mutex_unlock(&hdev->mgmt_pending_lock);
+ return cmd;
+ }
+
+ mutex_unlock(&hdev->mgmt_pending_lock);
+
return NULL;
}
@@ -3566,10 +3608,10 @@ void mgmt_smp_complete(struct hci_conn *conn, bool complete)
u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED;
struct mgmt_pending_cmd *cmd;
- cmd = find_pairing(conn);
+ cmd = remove_pairing(conn);
if (cmd) {
cmd->cmd_complete(cmd, status);
- mgmt_pending_remove(cmd);
+ mgmt_pending_free(cmd);
}
}
@@ -3579,14 +3621,14 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
BT_DBG("status %u", status);
- cmd = find_pairing(conn);
+ cmd = remove_pairing(conn);
if (!cmd) {
BT_DBG("Unable to find a pending command");
return;
}
cmd->cmd_complete(cmd, mgmt_status(status));
- mgmt_pending_remove(cmd);
+ mgmt_pending_free(cmd);
}
static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
@@ -3598,14 +3640,14 @@ static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
if (!status)
return;
- cmd = find_pairing(conn);
+ cmd = remove_pairing(conn);
if (!cmd) {
BT_DBG("Unable to find a pending command");
return;
}
cmd->cmd_complete(cmd, mgmt_status(status));
- mgmt_pending_remove(cmd);
+ mgmt_pending_free(cmd);
}
static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -3762,23 +3804,17 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
- cmd = pending_find(MGMT_OP_PAIR_DEVICE, hdev);
+ cmd = remove_pairing_by_addr(hdev, &addr->bdaddr);
if (!cmd) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
MGMT_STATUS_INVALID_PARAMS);
goto unlock;
}
- conn = cmd->user_data;
-
- if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
- err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
- MGMT_STATUS_INVALID_PARAMS);
- goto unlock;
- }
+ conn = hci_conn_get(cmd->user_data);
cmd->cmd_complete(cmd, MGMT_STATUS_CANCELLED);
- mgmt_pending_remove(cmd);
+ mgmt_pending_free(cmd);
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0,
addr, sizeof(*addr));
@@ -3796,6 +3832,8 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
if (conn->conn_reason == CONN_REASON_PAIR_DEVICE)
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+ hci_conn_put(conn);
+
unlock:
hci_dev_unlock(hdev);
return err;
@@ -4043,6 +4081,12 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
+ if (hdev_is_powered(hdev) && pending_eir_or_class(hdev)) {
+ err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
+ MGMT_STATUS_BUSY);
+ goto failed;
+ }
+
memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name));
if (!hdev_is_powered(hdev)) {
@@ -10137,14 +10181,14 @@ void mgmt_auth_failed(struct hci_conn *conn, u8 hci_status)
ev.addr.type = link_to_bdaddr(conn->type, conn->dst_type);
ev.status = status;
- cmd = find_pairing(conn);
+ cmd = remove_pairing(conn);
mgmt_event(MGMT_EV_AUTH_FAILED, conn->hdev, &ev, sizeof(ev),
cmd ? cmd->sk : NULL);
if (cmd) {
cmd->cmd_complete(cmd, status);
- mgmt_pending_remove(cmd);
+ mgmt_pending_free(cmd);
}
}
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 75f7512dec54..2e8c080b4d9e 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -1795,6 +1795,11 @@ static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s,
return s;
}
+ if (skb->len < sizeof(*hdr) + 1) {
+ kfree_skb(skb);
+ return s;
+ }
+
dlci = __get_dlci(hdr->addr);
type = __get_type(hdr->ctrl);
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index c05f79b7aa31..3d4362a09df4 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -188,6 +188,9 @@ static void sco_sock_clear_timer(struct sock *sk)
}
/* ---- SCO connections ---- */
+/* Consumes a reference on @hcon, which the returned sco_conn owns until it is
+ * freed. On failure (NULL return) the reference is left for the caller to drop.
+ */
static struct sco_conn *sco_conn_add(struct hci_conn *hcon)
{
struct sco_conn *conn = hcon->sco_data;
@@ -198,6 +201,9 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon)
sco_conn_lock(conn);
conn->hcon = hcon;
sco_conn_unlock(conn);
+ } else {
+ /* conn already owns a reference on hcon */
+ hci_conn_drop(hcon);
}
return conn;
}
@@ -265,10 +271,8 @@ static void sco_conn_del(struct hci_conn *hcon, int err)
sco_conn_unlock(conn);
sco_conn_put(conn);
- if (!sk) {
- sco_conn_put(conn);
+ if (!sk)
return;
- }
/* Kill socket */
lock_sock(sk);
@@ -283,7 +287,7 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk,
{
BT_DBG("conn %p", conn);
- sco_pi(sk)->conn = conn;
+ sco_pi(sk)->conn = sco_conn_hold(conn);
conn->sk = sk;
if (parent)
@@ -366,15 +370,15 @@ static int sco_connect(struct sock *sk)
*/
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) {
release_sock(sk);
- hci_conn_drop(hcon);
+ sco_conn_put(conn);
err = -EBADFD;
goto unlock;
}
err = sco_chan_add(conn, sk, NULL);
+ sco_conn_put(conn);
if (err) {
release_sock(sk);
- hci_conn_drop(hcon);
goto unlock;
}
@@ -1452,8 +1456,6 @@ static void sco_conn_ready(struct sco_conn *conn)
bacpy(&sco_pi(sk)->src, &conn->hcon->src);
bacpy(&sco_pi(sk)->dst, &conn->hcon->dst);
- sco_conn_hold(conn);
- hci_conn_hold(conn->hcon);
__sco_chan_add(conn, sk, parent);
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
@@ -1509,10 +1511,12 @@ static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
if (!status) {
struct sco_conn *conn;
- conn = sco_conn_add(hcon);
+ conn = sco_conn_add(hci_conn_hold(hcon));
if (conn) {
sco_conn_ready(conn);
sco_conn_put(conn);
+ } else {
+ hci_conn_drop(hcon);
}
} else
sco_conn_del(hcon, bt_to_errno(status));