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 <sbajpai@codeaurora.org>
Signed-off-by: Mallikarjuna GB <gbmalli@codeaurora.org>
This commit is contained in:
Sumit Bajpai 2013-01-29 12:27:43 +05:30 committed by Iliyan Malchev
parent 0c537ac89b
commit 505bdc3bd1
2 changed files with 37 additions and 33 deletions

View file

@ -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)

View file

@ -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);