summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2009-02-06 23:35:19 +0100
committerMarcel Holtmann <marcel@holtmann.org>2009-02-27 06:14:41 +0100
commit984947dc64f82bc6cafa4d84ba1a139718f634a8 (patch)
treef85e4b260034f5b5a20af7a63900ee7de06fa7ad
parent657e17b03c80bec817975984d221bef716f83558 (diff)
Bluetooth: Fix race condition with L2CAP information request
When two L2CAP connections are requested quickly after the ACL link has been established there exists a window for a race condition where a connection request is sent before the information response has been received. Any connection request should only be sent after an exchange of the extended features mask has been finished. Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
-rw-r--r--include/net/bluetooth/l2cap.h3
-rw-r--r--net/bluetooth/l2cap.c17
2 files changed, 16 insertions, 4 deletions
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 29f720e6188..1c8cf3e9b1c 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -223,7 +223,8 @@ struct l2cap_conn {
};
#define L2CAP_INFO_CL_MTU_REQ_SENT 0x01
-#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x02
+#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x04
+#define L2CAP_INFO_FEAT_MASK_REQ_DONE 0x08
/* ----- L2CAP channel and socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 8a93dde4095..07fdbc7dd54 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -320,6 +320,9 @@ static void l2cap_do_start(struct sock *sk)
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+ if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
+ return;
+
if (l2cap_check_security(sk)) {
struct l2cap_conn_req req;
req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
@@ -455,6 +458,8 @@ static void l2cap_info_timeout(unsigned long arg)
conn->info_ident = 0;
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+
l2cap_conn_start(conn);
}
@@ -1787,6 +1792,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
cmd->ident == conn->info_ident) {
conn->info_ident = 0;
del_timer(&conn->info_timer);
+
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+
l2cap_conn_start(conn);
}
@@ -1857,7 +1865,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
l2cap_pi(sk)->ident = cmd->ident;
- if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+ if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_check_security(sk)) {
if (bt_sk(sk)->defer_setup) {
sk->sk_state = BT_CONNECT2;
@@ -2176,10 +2184,13 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
del_timer(&conn->info_timer);
- if (type == L2CAP_IT_FEAT_MASK)
+ if (type == L2CAP_IT_FEAT_MASK) {
conn->feat_mask = get_unaligned_le32(rsp->data);
- l2cap_conn_start(conn);
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+
+ l2cap_conn_start(conn);
+ }
return 0;
}