From 505bdc3bd1c292630cc50ea433ecb0c5cf3700a6 Mon Sep 17 00:00:00 2001 From: Sumit Bajpai Date: Tue, 29 Jan 2013 12:27:43 +0530 Subject: [PATCH] Bluetooth: hidp: safely acquire hci connection Claim device lock to safely enumerate hci connection list and bump hci connection proxy device ref count simultaneously. Also it prevents kernel crash due to null pointer conn->hdev in deleting conn entry from sysfs. CRs-Fixed: 446403 Change-Id: I5ec4283d359f95e47711dff171d0619b15263349 Signed-off-by: Sumit Bajpai Signed-off-by: Mallikarjuna GB --- net/bluetooth/hci_sysfs.c | 3 +- net/bluetooth/hidp/core.c | 67 ++++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 0a4c7d60222..255419e6b59 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -166,7 +166,8 @@ void hci_conn_del_sysfs(struct hci_conn *conn) { BT_DBG("conn %p", conn); - queue_work(conn->hdev->workqueue, &conn->work_del); + if (conn->hdev) + queue_work(conn->hdev->workqueue, &conn->work_del); } static inline char *host_bustostr(int bus) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index d80c0e33ab5..2c4ab786ae5 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -93,40 +93,15 @@ static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr) return NULL; } -static struct device *hidp_get_device(struct hidp_session *session) -{ - bdaddr_t *dst = &session->bdaddr; - - struct device *device = NULL; - struct hci_dev *hdev; - - hdev = hci_get_route(dst, BDADDR_ANY); - if (!hdev) - return NULL; - - session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - if (session->conn) - device = &session->conn->dev; - - hci_dev_put(hdev); - - return device; -} - static void __hidp_link_session(struct hidp_session *session) { __module_get(THIS_MODULE); list_add(&session->list, &hidp_session_list); - - hci_conn_hold_device(session->conn); } static void __hidp_unlink_session(struct hidp_session *session) { - struct device *dev; - - dev = hidp_get_device(session); - if (dev) + if (session->conn) hci_conn_put_device(session->conn); list_del(&session->list); @@ -660,6 +635,28 @@ static int hidp_session(void *arg) return 0; } +static struct hci_conn *hidp_get_connection(struct hidp_session *session) +{ + bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src; + bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; + struct hci_conn *conn; + struct hci_dev *hdev; + + hdev = hci_get_route(dst, src); + if (!hdev) + return NULL; + + hci_dev_lock_bh(hdev); + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); + if (conn) + hci_conn_hold_device(conn); + hci_dev_unlock_bh(hdev); + + hci_dev_put(hdev); + + return conn; +} + static int hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req) { @@ -707,7 +704,7 @@ static int hidp_setup_input(struct hidp_session *session, input->relbit[0] |= BIT_MASK(REL_WHEEL); } - input->dev.parent = hidp_get_device(session); + input->dev.parent = &session->conn->dev; input->event = hidp_input_event; @@ -808,7 +805,7 @@ static int hidp_setup_hid(struct hidp_session *session, strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64); strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64); - hid->dev.parent = hidp_get_device(session); + hid->dev.parent = &session->conn->dev; hid->ll_driver = &hidp_hid_driver; hid->hid_output_raw_report = hidp_output_raw_report; @@ -866,6 +863,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, session->intr_sock = intr_sock; session->state = BT_CONNECTED; + session->conn = hidp_get_connection(session); + if (!session->conn) { + err = -ENOTCONN; + goto failed; + } + setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session); skb_queue_head_init(&session->ctrl_transmit); @@ -874,6 +877,8 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); session->idle_to = req->idle_to; + __hidp_link_session(session); + if (req->rd_size > 0) { err = hidp_setup_hid(session, req); if (err && err != -ENODEV) @@ -886,8 +891,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, goto purge; } - __hidp_link_session(session); - hidp_set_timer(session); err = kernel_thread(hidp_session, session, CLONE_KERNEL); @@ -909,8 +912,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, unlink: hidp_del_timer(session); - __hidp_unlink_session(session); - if (session->input) { input_unregister_device(session->input); session->input = NULL; @@ -925,6 +926,8 @@ unlink: session->rd_data = NULL; purge: + __hidp_unlink_session(session); + skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit);