* [RFC] Bluetooth: hidp: Check for valid ACL connection
@ 2011-08-19 14:41 David Herrmann
2011-08-30 13:40 ` Peter Hurley
0 siblings, 1 reply; 3+ messages in thread
From: David Herrmann @ 2011-08-19 14:41 UTC (permalink / raw)
To: linux-bluetooth; +Cc: padovan, marcel, peter, dh.herrmann
Check for valid ACL connection before creating an hid device.
__hidp_link_session() needs session->conn but hidp_add_connection does not check
whether session->conn is set.
Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
I recently got several kernel panics in hidp_add_connection when creating a
bluetooth HID device. I got these panics when the device shut down *while*
trying to initiate the HID connection.
This fix aborts the HID device creation if the ACL connection is disconnected.
This fixes the problem for me but I don't know whether there is still a
race-condition between obtainig session->conn and calling hci_conn_hold_device
in __hidp_link_session().
Also I think it would make the code nicer when calling hidp_get_device() in
hidp_add_connection and hidp_setup_input/hid just retrieves the parent-device
from session->conn.
Anyway, you can find my kernel oops message here:
https://gist.github.com/1156759
As far as I got, it is caused by:
hidp_add_connection()
-> __hidp_link_session()
-> hci_conn_hold_device()
-> atomic_inc()
-> test_and_set_bit()
Regards
David
net/bluetooth/hidp/core.c | 11 +++++++++++
1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index fb68f34..baec330 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -837,6 +837,11 @@ static int hidp_setup_input(struct hidp_session *session,
}
input->dev.parent = hidp_get_device(session);
+ if (!input->dev.parent) {
+ input_free_device(input);
+ session->input = NULL;
+ return -ENODEV;
+ }
input->event = hidp_input_event;
@@ -941,6 +946,9 @@ static int hidp_setup_hid(struct hidp_session *session,
strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
hid->dev.parent = hidp_get_device(session);
+ if (!hid->dev.parent)
+ goto dev_err;
+
hid->ll_driver = &hidp_hid_driver;
hid->hid_get_raw_report = hidp_get_raw_report;
@@ -948,6 +956,9 @@ static int hidp_setup_hid(struct hidp_session *session,
return 0;
+dev_err:
+ hid_destroy_device(hid);
+ session->hid = NULL;
fault:
kfree(session->rd_data);
session->rd_data = NULL;
--
1.7.6
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [RFC] Bluetooth: hidp: Check for valid ACL connection 2011-08-19 14:41 [RFC] Bluetooth: hidp: Check for valid ACL connection David Herrmann @ 2011-08-30 13:40 ` Peter Hurley 2011-08-30 14:13 ` David Herrmann 0 siblings, 1 reply; 3+ messages in thread From: Peter Hurley @ 2011-08-30 13:40 UTC (permalink / raw) To: David Herrmann Cc: linux-bluetooth@vger.kernel.org, padovan@profusion.mobi, marcel@holtmann.org Hi David, On Fri, 2011-08-19 at 10:41 -0400, David Herrmann wrote: > Also I think it would make the code nicer when calling hidp_get_device() = in > hidp_add_connection and hidp_setup_input/hid just retrieves the parent-de= vice > from session->conn. What about something like this instead? This: 1. moves the hidp_get_device out into hidp_add_connection 2. renames hidp_get_device to hidp_find_connection 3. checks for null connection value return 4. performs the connection list access with device lock held 5. bumps the hci connection device hold with device lock held diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index e02c6a2..f6db935 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -97,8 +97,6 @@ static void __hidp_link_session(struct hidp_session *sess= ion) { __module_get(THIS_MODULE); list_add(&session->list, &hidp_session_list); - - hci_conn_hold_device(session->conn); } =20 static void __hidp_unlink_session(struct hidp_session *session) @@ -763,24 +761,26 @@ static int hidp_session(void *arg) return 0; } =20 -static struct device *hidp_get_device(struct hidp_session *session) +static struct hci_conn *hidp_find_connection(struct hidp_session *session) { bdaddr_t *src =3D &bt_sk(session->ctrl_sock->sk)->src; bdaddr_t *dst =3D &bt_sk(session->ctrl_sock->sk)->dst; - struct device *device =3D NULL; + struct hci_conn *conn; struct hci_dev *hdev; =20 hdev =3D hci_get_route(dst, src); if (!hdev) return NULL; =20 - session->conn =3D hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - if (session->conn) - device =3D &session->conn->dev; + hci_dev_lock_bh(hdev); + conn =3D hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); + if (conn) + hci_conn_hold_device(conn); + hci_dev_unlock_bh(hdev); =20 hci_dev_put(hdev); =20 - return device; + return conn; } =20 static int hidp_setup_input(struct hidp_session *session, @@ -830,7 +830,7 @@ static int hidp_setup_input(struct hidp_session *sessio= n, input->relbit[0] |=3D BIT_MASK(REL_WHEEL); } =20 - input->dev.parent =3D hidp_get_device(session); + input->dev.parent =3D &session->conn->dev; =20 input->event =3D hidp_input_event; =20 @@ -934,7 +934,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); =20 - hid->dev.parent =3D hidp_get_device(session); + hid->dev.parent =3D &session->conn->dev; hid->ll_driver =3D &hidp_hid_driver; =20 hid->hid_get_raw_report =3D hidp_get_raw_report; @@ -975,6 +975,10 @@ int hidp_add_connection(struct hidp_connadd_req *req, = struct socket *ctrl_sock, goto failed; } =20 + session->conn =3D hidp_find_connection(session); + if (!session->conn) + goto failed; + bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst); =20 session->ctrl_mtu =3D min_t(uint, l2cap_pi(ctrl_sock->sk)->chan->omtu, @@ -1000,6 +1004,8 @@ int hidp_add_connection(struct hidp_connadd_req *req,= struct socket *ctrl_sock, session->flags =3D req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); session->idle_to =3D req->idle_to; =20 + __hidp_link_session(session); + if (req->rd_size > 0) { err =3D hidp_setup_hid(session, req); if (err && err !=3D -ENODEV) @@ -1012,8 +1018,6 @@ int hidp_add_connection(struct hidp_connadd_req *req,= struct socket *ctrl_sock, goto purge; } =20 - __hidp_link_session(session); - hidp_set_timer(session); =20 if (session->hid) { @@ -1062,8 +1066,6 @@ int hidp_add_connection(struct hidp_connadd_req *req,= struct socket *ctrl_sock, unlink: hidp_del_timer(session); =20 - __hidp_unlink_session(session); - if (session->input) { input_unregister_device(session->input); session->input =3D NULL; @@ -1076,6 +1078,8 @@ unlink: session->rd_data =3D NULL; =20 purge: + __hidp_unlink_session(session); + skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit); =20 --=20 Regards, Peter Hurley ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [RFC] Bluetooth: hidp: Check for valid ACL connection 2011-08-30 13:40 ` Peter Hurley @ 2011-08-30 14:13 ` David Herrmann 0 siblings, 0 replies; 3+ messages in thread From: David Herrmann @ 2011-08-30 14:13 UTC (permalink / raw) To: Peter Hurley Cc: linux-bluetooth@vger.kernel.org, padovan@profusion.mobi, marcel@holtmann.org Hi Peter On Tue, Aug 30, 2011 at 3:40 PM, Peter Hurley <peter@hurleysoftware.com> wr= ote: > Hi David, > > On Fri, 2011-08-19 at 10:41 -0400, David Herrmann wrote: >> Also I think it would make the code nicer when calling hidp_get_device()= in >> hidp_add_connection and hidp_setup_input/hid just retrieves the parent-d= evice >> from session->conn. > > What about something like this instead? > > This: > 1. moves the hidp_get_device out into hidp_add_connection > 2. renames hidp_get_device to hidp_find_connection > 3. checks for null connection value return > 4. performs the connection list access with device lock held > 5. bumps the hci connection device hold with device lock held > > > diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c > index e02c6a2..f6db935 100644 > --- a/net/bluetooth/hidp/core.c > +++ b/net/bluetooth/hidp/core.c > @@ -97,8 +97,6 @@ static void __hidp_link_session(struct hidp_session *se= ssion) > =A0{ > =A0 =A0 =A0 =A0__module_get(THIS_MODULE); > =A0 =A0 =A0 =A0list_add(&session->list, &hidp_session_list); > - > - =A0 =A0 =A0 hci_conn_hold_device(session->conn); > =A0} > > =A0static void __hidp_unlink_session(struct hidp_session *session) > @@ -763,24 +761,26 @@ static int hidp_session(void *arg) > =A0 =A0 =A0 =A0return 0; > =A0} > > -static struct device *hidp_get_device(struct hidp_session *session) > +static struct hci_conn *hidp_find_connection(struct hidp_session *sessio= n) > =A0{ > =A0 =A0 =A0 =A0bdaddr_t *src =3D &bt_sk(session->ctrl_sock->sk)->src; > =A0 =A0 =A0 =A0bdaddr_t *dst =3D &bt_sk(session->ctrl_sock->sk)->dst; > - =A0 =A0 =A0 struct device *device =3D NULL; > + =A0 =A0 =A0 struct hci_conn *conn; > =A0 =A0 =A0 =A0struct hci_dev *hdev; > > =A0 =A0 =A0 =A0hdev =3D hci_get_route(dst, src); > =A0 =A0 =A0 =A0if (!hdev) > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0return NULL; > > - =A0 =A0 =A0 session->conn =3D hci_conn_hash_lookup_ba(hdev, ACL_LINK, d= st); > - =A0 =A0 =A0 if (session->conn) > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 device =3D &session->conn->dev; > + =A0 =A0 =A0 hci_dev_lock_bh(hdev); > + =A0 =A0 =A0 conn =3D hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); > + =A0 =A0 =A0 if (conn) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 hci_conn_hold_device(conn); > + =A0 =A0 =A0 hci_dev_unlock_bh(hdev); > > =A0 =A0 =A0 =A0hci_dev_put(hdev); > > - =A0 =A0 =A0 return device; > + =A0 =A0 =A0 return conn; > =A0} > > =A0static int hidp_setup_input(struct hidp_session *session, > @@ -830,7 +830,7 @@ static int hidp_setup_input(struct hidp_session *sess= ion, > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0input->relbit[0] |=3D BIT_MASK(REL_WHEEL); > =A0 =A0 =A0 =A0} > > - =A0 =A0 =A0 input->dev.parent =3D hidp_get_device(session); > + =A0 =A0 =A0 input->dev.parent =3D &session->conn->dev; > > =A0 =A0 =A0 =A0input->event =3D hidp_input_event; > > @@ -934,7 +934,7 @@ static int hidp_setup_hid(struct hidp_session *sessio= n, > =A0 =A0 =A0 =A0strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)-= >src), 64); > =A0 =A0 =A0 =A0strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)-= >dst), 64); > > - =A0 =A0 =A0 hid->dev.parent =3D hidp_get_device(session); > + =A0 =A0 =A0 hid->dev.parent =3D &session->conn->dev; > =A0 =A0 =A0 =A0hid->ll_driver =3D &hidp_hid_driver; > > =A0 =A0 =A0 =A0hid->hid_get_raw_report =3D hidp_get_raw_report; > @@ -975,6 +975,10 @@ int hidp_add_connection(struct hidp_connadd_req *req= , struct socket *ctrl_sock, > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto failed; > =A0 =A0 =A0 =A0} > > + =A0 =A0 =A0 session->conn =3D hidp_find_connection(session); > + =A0 =A0 =A0 if (!session->conn) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto failed; > + > =A0 =A0 =A0 =A0bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst); > > =A0 =A0 =A0 =A0session->ctrl_mtu =3D min_t(uint, l2cap_pi(ctrl_sock->sk)-= >chan->omtu, > @@ -1000,6 +1004,8 @@ int hidp_add_connection(struct hidp_connadd_req *re= q, struct socket *ctrl_sock, > =A0 =A0 =A0 =A0session->flags =A0 =3D req->flags & (1 << HIDP_BLUETOOTH_V= ENDOR_ID); > =A0 =A0 =A0 =A0session->idle_to =3D req->idle_to; > > + =A0 =A0 =A0 __hidp_link_session(session); > + > =A0 =A0 =A0 =A0if (req->rd_size > 0) { > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0err =3D hidp_setup_hid(session, req); > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (err && err !=3D -ENODEV) > @@ -1012,8 +1018,6 @@ int hidp_add_connection(struct hidp_connadd_req *re= q, struct socket *ctrl_sock, > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0goto purge; > =A0 =A0 =A0 =A0} > > - =A0 =A0 =A0 __hidp_link_session(session); > - > =A0 =A0 =A0 =A0hidp_set_timer(session); > > =A0 =A0 =A0 =A0if (session->hid) { > @@ -1062,8 +1066,6 @@ int hidp_add_connection(struct hidp_connadd_req *re= q, struct socket *ctrl_sock, > =A0unlink: > =A0 =A0 =A0 =A0hidp_del_timer(session); > > - =A0 =A0 =A0 __hidp_unlink_session(session); > - > =A0 =A0 =A0 =A0if (session->input) { > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0input_unregister_device(session->input); > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0session->input =3D NULL; > @@ -1076,6 +1078,8 @@ unlink: > =A0 =A0 =A0 =A0session->rd_data =3D NULL; > > =A0purge: > + =A0 =A0 =A0 __hidp_unlink_session(session); > + > =A0 =A0 =A0 =A0skb_queue_purge(&session->ctrl_transmit); > =A0 =A0 =A0 =A0skb_queue_purge(&session->intr_transmit); > > -- Great! This fixes the race-condition I mentioned earlier, error-recovery also seems fine here. Thanks! After Marcel or Gustavo commented on that, can you resend it with a proper commit-message? Or should I resend it? I am fine with you doing that, here's my signed-off line anyway: Signed-off-by: David Herrmann <dh.herrmann@googlemail.com> > Regards, > Peter Hurley Regards David ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2011-08-30 14:13 UTC | newest] Thread overview: 3+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-08-19 14:41 [RFC] Bluetooth: hidp: Check for valid ACL connection David Herrmann 2011-08-30 13:40 ` Peter Hurley 2011-08-30 14:13 ` David Herrmann
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).