From 984947dc64f82bc6cafa4d84ba1a139718f634a8 Mon Sep 17 00:00:00 2001
From: Marcel Holtmann <marcel@holtmann.org>
Date: Fri, 6 Feb 2009 23:35:19 +0100
Subject: 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>
---
 net/bluetooth/l2cap.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

(limited to 'net/bluetooth')

diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 8a93dde4095b..07fdbc7dd54d 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;
 }
-- 
cgit v1.2.3