Skip to content

Commit f161dd4

Browse files
author
Johan Hedberg
committed
Bluetooth: Fix hci_conn reference counting for auto-connections
Recently the LE passive scanning and auto-connections feature was introduced. It uses the hci_connect_le() API which returns a hci_conn along with a reference count to that object. All previous users would tie this returned reference to some existing object, such as an L2CAP channel, and there'd be no leaked references this way. For auto-connections however the reference was returned but not stored anywhere, leaving established connections with one higher reference count than they should have. Instead of playing special tricks with hci_conn_hold/drop this patch associates the returned reference from hci_connect_le() with the object that in practice does own this reference, i.e. the hci_conn_params struct that caused us to initiate a connection in the first place. Once the connection is established or fails to establish this reference is removed appropriately. One extra thing needed is to call hci_pend_le_actions_clear() before calling hci_conn_hash_flush() so that the reference is cleared before the hci_conn objects are fully removed. Signed-off-by: Johan Hedberg <[email protected]> Signed-off-by: Marcel Holtmann <[email protected]>
1 parent 6697dab commit f161dd4

File tree

4 files changed

+37
-4
lines changed

4 files changed

+37
-4
lines changed

include/net/bluetooth/hci_core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,8 @@ struct hci_conn_params {
464464
HCI_AUTO_CONN_ALWAYS,
465465
HCI_AUTO_CONN_LINK_LOSS,
466466
} auto_connect;
467+
468+
struct hci_conn *conn;
467469
};
468470

469471
extern struct list_head hci_dev_list;

net/bluetooth/hci_conn.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,14 @@ EXPORT_SYMBOL(hci_get_route);
589589
void hci_le_conn_failed(struct hci_conn *conn, u8 status)
590590
{
591591
struct hci_dev *hdev = conn->hdev;
592+
struct hci_conn_params *params;
593+
594+
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
595+
conn->dst_type);
596+
if (params && params->conn) {
597+
hci_conn_drop(params->conn);
598+
params->conn = NULL;
599+
}
592600

593601
conn->state = BT_CLOSED;
594602

net/bluetooth/hci_core.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2536,8 +2536,13 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev)
25362536
{
25372537
struct hci_conn_params *p;
25382538

2539-
list_for_each_entry(p, &hdev->le_conn_params, list)
2539+
list_for_each_entry(p, &hdev->le_conn_params, list) {
2540+
if (p->conn) {
2541+
hci_conn_drop(p->conn);
2542+
p->conn = NULL;
2543+
}
25402544
list_del_init(&p->action);
2545+
}
25412546

25422547
BT_DBG("All LE pending actions cleared");
25432548
}
@@ -2578,8 +2583,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
25782583

25792584
hci_dev_lock(hdev);
25802585
hci_inquiry_cache_flush(hdev);
2581-
hci_conn_hash_flush(hdev);
25822586
hci_pend_le_actions_clear(hdev);
2587+
hci_conn_hash_flush(hdev);
25832588
hci_dev_unlock(hdev);
25842589

25852590
hci_notify(hdev, HCI_DEV_DOWN);
@@ -3727,6 +3732,9 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type)
37273732
if (!params)
37283733
return;
37293734

3735+
if (params->conn)
3736+
hci_conn_drop(params->conn);
3737+
37303738
list_del(&params->action);
37313739
list_del(&params->list);
37323740
kfree(params);
@@ -3757,6 +3765,8 @@ void hci_conn_params_clear_all(struct hci_dev *hdev)
37573765
struct hci_conn_params *params, *tmp;
37583766

37593767
list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) {
3768+
if (params->conn)
3769+
hci_conn_drop(params->conn);
37603770
list_del(&params->action);
37613771
list_del(&params->list);
37623772
kfree(params);

net/bluetooth/hci_event.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4221,8 +4221,13 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
42214221
hci_proto_connect_cfm(conn, ev->status);
42224222

42234223
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
4224-
if (params)
4224+
if (params) {
42254225
list_del_init(&params->action);
4226+
if (params->conn) {
4227+
hci_conn_drop(params->conn);
4228+
params->conn = NULL;
4229+
}
4230+
}
42264231

42274232
unlock:
42284233
hci_update_background_scan(hdev);
@@ -4304,8 +4309,16 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
43044309

43054310
conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
43064311
HCI_LE_AUTOCONN_TIMEOUT, HCI_ROLE_MASTER);
4307-
if (!IS_ERR(conn))
4312+
if (!IS_ERR(conn)) {
4313+
/* Store the pointer since we don't really have any
4314+
* other owner of the object besides the params that
4315+
* triggered it. This way we can abort the connection if
4316+
* the parameters get removed and keep the reference
4317+
* count consistent once the connection is established.
4318+
*/
4319+
params->conn = conn;
43084320
return;
4321+
}
43094322

43104323
switch (PTR_ERR(conn)) {
43114324
case -EBUSY:

0 commit comments

Comments
 (0)