CHROMIUM: Bluetooth: Use addr instead of hci_conn in LE Connection complete

Connections may be cleanup while waiting for the commands to complete.
If this happens, it might be the reason we see crashes below which
indicates that the |conn->hdev| is NULL, which can be caused by freeing
the memory of the |hci_conn| earliar.

This fixes the issue by passing the address data instead of the hci_conn
to the HCI command sync work so that we can check if such hci_conn
exists before accessing it.

Call Trace:
? __die_body+0x1f/0x63
? no_context+0x32f/0x506
? exc_page_fault+0x2d8/0x400
? __hci_cmd_sync_sk+0x464/0x4b7 [bluetooth (HASH:202e 2)]
? asm_exc_page_fault+0x1e/0x30
? hci_connect_le_sync+0x49/0x49 [bluetooth (HASH:202e 2)]
? hci_pend_le_action_lookup+0x12/0x68 [bluetooth (HASH:202e 2)]
hci_connect_le_scan_cleanup+0x82/0x252 [bluetooth (HASH:202e 2)]
create_le_conn_complete+0xc9/0xdb [bluetooth (HASH:202e 2)]
hci_cmd_sync_work+0x11a/0x15b [bluetooth (HASH:202e 2)]
process_one_work+0x18d/0x416
worker_thread+0x11a/0x289
kthread+0x13e/0x14f
? process_one_work+0x416/0x416
? kthread_blkcg+0x31/0x31
ret_from_fork+0x1f/0x30

UPSTREAM-TASK=b:303584676
BUG=b:204408624
TEST=run CUJ on hatch v5.15 with MX keys.
TEST=run bluetooth_AdapterLEHealth on hatch v5.15.

Change-Id: Ieb313a0aa7140a8478b3e1e5c0d8c6ccc89d1b6e
Signed-off-by: Yun-Hao Chung <howardchung@google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/4914806
Reviewed-by: Archie Pusaka <apusaka@chromium.org>
Tested-by: Yun-Hao Chung <howardchung@chromium.org>
Commit-Queue: Yun-Hao Chung <howardchung@chromium.org>
(cherry picked from commit f2bff6a8b2f0080467acf8bc87d5b80fc058a424)
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/4957717
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index df80117..a28342f 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -41,6 +41,11 @@
 	u8  retrans_effort;
 };
 
+struct le_conn_data {
+	bdaddr_t dst;
+	u8 dst_type;
+};
+
 static const struct sco_param esco_param_cvsd[] = {
 	{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x000a,	0x01 }, /* S3 */
 	{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007,	0x01 }, /* S2 */
@@ -811,12 +816,27 @@
 
 static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
 {
-	struct hci_conn *conn = data;
+	struct le_conn_data *conn_data = data;
+	struct hci_conn *conn;
 
 	bt_dev_dbg(hdev, "err %d", err);
 
+	if (!conn_data) {
+		bt_dev_err(hdev, "conn_data is NULL");
+		return;
+	}
+
 	hci_dev_lock(hdev);
 
+	conn = hci_conn_hash_lookup_le(hdev, &conn_data->dst, conn_data->dst_type);
+
+	if (!conn) {
+		bt_dev_err(hdev,
+			   "can't find conn with addr:%pMR,type:%d, skip the connection",
+			   &conn_data->dst, conn_data->dst_type);
+		goto done;
+	}
+
 	if (!err) {
 		hci_connect_le_scan_cleanup(conn, 0x00);
 		goto done;
@@ -831,12 +851,28 @@
 	hci_conn_failed(conn, err);
 
 done:
+	kfree(conn_data);
 	hci_dev_unlock(hdev);
 }
 
 static int hci_connect_le_sync(struct hci_dev *hdev, void *data)
 {
-	struct hci_conn *conn = data;
+	struct le_conn_data *conn_data = data;
+	struct hci_conn *conn;
+
+	if (!conn_data) {
+		bt_dev_err(hdev, "conn_data is NULL");
+		return -ECONNABORTED;
+	}
+
+	conn = hci_conn_hash_lookup_le(hdev, &conn_data->dst, conn_data->dst_type);
+
+	if (!conn) {
+		bt_dev_err(hdev,
+			   "can't find conn with addr:%pMR,type:%d, skip the connection",
+			   &conn_data->dst, conn_data->dst_type);
+		return -ECONNABORTED;
+	}
 
 	bt_dev_dbg(hdev, "conn %p", conn);
 
@@ -849,6 +885,7 @@
 {
 	struct hci_conn *conn;
 	struct smp_irk *irk;
+	struct le_conn_data *conn_data;
 	int err;
 
 	/* Let's make sure that le is enabled.*/
@@ -912,10 +949,17 @@
 	conn->state = BT_CONNECT;
 	clear_bit(HCI_CONN_SCANNING, &conn->flags);
 
-	err = hci_cmd_sync_queue(hdev, hci_connect_le_sync, conn,
+	conn_data = kmalloc(sizeof(*conn_data), GFP_KERNEL);
+	if (!conn_data)
+		return ERR_PTR(-ENOMEM);
+	bacpy(&conn_data->dst, dst);
+	conn_data->dst_type = dst_type;
+
+	err = hci_cmd_sync_queue(hdev, hci_connect_le_sync, conn_data,
 				 create_le_conn_complete);
 	if (err) {
 		hci_conn_del(conn);
+		kfree(conn_data);
 		return ERR_PTR(err);
 	}