--- /dev/null
+From ab4eedb790cae44313759b50fe47da285e2519d5 Mon Sep 17 00:00:00 2001
+From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Date: Thu, 6 Feb 2025 15:54:45 -0500
+Subject: Bluetooth: L2CAP: Fix corrupted list in hci_chan_del
+
+From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+
+commit ab4eedb790cae44313759b50fe47da285e2519d5 upstream.
+
+This fixes the following trace by reworking the locking of l2cap_conn
+so instead of only locking when changing the chan_l list this promotes
+chan_lock to a general lock of l2cap_conn so whenever it is being held
+it would prevents the likes of l2cap_conn_del to run:
+
+list_del corruption, ffff888021297e00->prev is LIST_POISON2 (dead000000000122)
+------------[ cut here ]------------
+kernel BUG at lib/list_debug.c:61!
+Oops: invalid opcode: 0000 [#1] PREEMPT SMP KASAN PTI
+CPU: 1 UID: 0 PID: 5896 Comm: syz-executor213 Not tainted 6.14.0-rc1-next-20250204-syzkaller #0
+Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 12/27/2024
+RIP: 0010:__list_del_entry_valid_or_report+0x12c/0x190 lib/list_debug.c:59
+Code: 8c 4c 89 fe 48 89 da e8 32 8c 37 fc 90 0f 0b 48 89 df e8 27 9f 14 fd 48 c7 c7 a0 c0 60 8c 4c 89 fe 48 89 da e8 15 8c 37 fc 90 <0f> 0b 4c 89 e7 e8 0a 9f 14 fd 42 80 3c 2b 00 74 08 4c 89 e7 e8 cb
+RSP: 0018:ffffc90003f6f998 EFLAGS: 00010246
+RAX: 000000000000004e RBX: dead000000000122 RCX: 01454d423f7fbf00
+RDX: 0000000000000000 RSI: 0000000080000000 RDI: 0000000000000000
+RBP: dffffc0000000000 R08: ffffffff819f077c R09: 1ffff920007eded0
+R10: dffffc0000000000 R11: fffff520007eded1 R12: dead000000000122
+R13: dffffc0000000000 R14: ffff8880352248d8 R15: ffff888021297e00
+FS: 00007f7ace6686c0(0000) GS:ffff8880b8700000(0000) knlGS:0000000000000000
+CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+CR2: 00007f7aceeeb1d0 CR3: 000000003527c000 CR4: 00000000003526f0
+DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
+DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
+Call Trace:
+ <TASK>
+ __list_del_entry_valid include/linux/list.h:124 [inline]
+ __list_del_entry include/linux/list.h:215 [inline]
+ list_del_rcu include/linux/rculist.h:168 [inline]
+ hci_chan_del+0x70/0x1b0 net/bluetooth/hci_conn.c:2858
+ l2cap_conn_free net/bluetooth/l2cap_core.c:1816 [inline]
+ kref_put include/linux/kref.h:65 [inline]
+ l2cap_conn_put+0x70/0xe0 net/bluetooth/l2cap_core.c:1830
+ l2cap_sock_shutdown+0xa8a/0x1020 net/bluetooth/l2cap_sock.c:1377
+ l2cap_sock_release+0x79/0x1d0 net/bluetooth/l2cap_sock.c:1416
+ __sock_release net/socket.c:642 [inline]
+ sock_close+0xbc/0x240 net/socket.c:1393
+ __fput+0x3e9/0x9f0 fs/file_table.c:448
+ task_work_run+0x24f/0x310 kernel/task_work.c:227
+ ptrace_notify+0x2d2/0x380 kernel/signal.c:2522
+ ptrace_report_syscall include/linux/ptrace.h:415 [inline]
+ ptrace_report_syscall_exit include/linux/ptrace.h:477 [inline]
+ syscall_exit_work+0xc7/0x1d0 kernel/entry/common.c:173
+ syscall_exit_to_user_mode_prepare kernel/entry/common.c:200 [inline]
+ __syscall_exit_to_user_mode_work kernel/entry/common.c:205 [inline]
+ syscall_exit_to_user_mode+0x24a/0x340 kernel/entry/common.c:218
+ do_syscall_64+0x100/0x230 arch/x86/entry/common.c:89
+ entry_SYSCALL_64_after_hwframe+0x77/0x7f
+RIP: 0033:0x7f7aceeaf449
+Code: 28 00 00 00 75 05 48 83 c4 28 c3 e8 41 19 00 00 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48
+RSP: 002b:00007f7ace668218 EFLAGS: 00000246 ORIG_RAX: 000000000000002a
+RAX: fffffffffffffffc RBX: 00007f7acef39328 RCX: 00007f7aceeaf449
+RDX: 000000000000000e RSI: 0000000020000100 RDI: 0000000000000004
+RBP: 00007f7acef39320 R08: 0000000000000000 R09: 0000000000000000
+R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000003
+R13: 0000000000000004 R14: 00007f7ace668670 R15: 000000000000000b
+ </TASK>
+Modules linked in:
+---[ end trace 0000000000000000 ]---
+RIP: 0010:__list_del_entry_valid_or_report+0x12c/0x190 lib/list_debug.c:59
+Code: 8c 4c 89 fe 48 89 da e8 32 8c 37 fc 90 0f 0b 48 89 df e8 27 9f 14 fd 48 c7 c7 a0 c0 60 8c 4c 89 fe 48 89 da e8 15 8c 37 fc 90 <0f> 0b 4c 89 e7 e8 0a 9f 14 fd 42 80 3c 2b 00 74 08 4c 89 e7 e8 cb
+RSP: 0018:ffffc90003f6f998 EFLAGS: 00010246
+RAX: 000000000000004e RBX: dead000000000122 RCX: 01454d423f7fbf00
+RDX: 0000000000000000 RSI: 0000000080000000 RDI: 0000000000000000
+RBP: dffffc0000000000 R08: ffffffff819f077c R09: 1ffff920007eded0
+R10: dffffc0000000000 R11: fffff520007eded1 R12: dead000000000122
+R13: dffffc0000000000 R14: ffff8880352248d8 R15: ffff888021297e00
+FS: 00007f7ace6686c0(0000) GS:ffff8880b8600000(0000) knlGS:0000000000000000
+CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+CR2: 00007f7acef05b08 CR3: 000000003527c000 CR4: 00000000003526f0
+DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
+DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
+
+Reported-by: syzbot+10bd8fe6741eedd2be2e@syzkaller.appspotmail.com
+Tested-by: syzbot+10bd8fe6741eedd2be2e@syzkaller.appspotmail.com
+Fixes: b4f82f9ed43a ("Bluetooth: L2CAP: Fix slab-use-after-free Read in l2cap_send_cmd")
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/bluetooth/l2cap.h | 3
+ net/bluetooth/l2cap_core.c | 138 ++++++++++++++----------------------------
+ net/bluetooth/l2cap_sock.c | 15 ++--
+ 3 files changed, 58 insertions(+), 98 deletions(-)
+
+--- a/include/net/bluetooth/l2cap.h
++++ b/include/net/bluetooth/l2cap.h
+@@ -668,7 +668,7 @@ struct l2cap_conn {
+ struct l2cap_chan *smp;
+
+ struct list_head chan_l;
+- struct mutex chan_lock;
++ struct mutex lock;
+ struct kref ref;
+ struct list_head users;
+ };
+@@ -970,6 +970,7 @@ void l2cap_chan_del(struct l2cap_chan *c
+ void l2cap_send_conn_req(struct l2cap_chan *chan);
+
+ struct l2cap_conn *l2cap_conn_get(struct l2cap_conn *conn);
++struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *conn);
+ void l2cap_conn_put(struct l2cap_conn *conn);
+
+ int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user);
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -119,7 +119,6 @@ static struct l2cap_chan *l2cap_get_chan
+ {
+ struct l2cap_chan *c;
+
+- mutex_lock(&conn->chan_lock);
+ c = __l2cap_get_chan_by_scid(conn, cid);
+ if (c) {
+ /* Only lock if chan reference is not 0 */
+@@ -127,7 +126,6 @@ static struct l2cap_chan *l2cap_get_chan
+ if (c)
+ l2cap_chan_lock(c);
+ }
+- mutex_unlock(&conn->chan_lock);
+
+ return c;
+ }
+@@ -140,7 +138,6 @@ static struct l2cap_chan *l2cap_get_chan
+ {
+ struct l2cap_chan *c;
+
+- mutex_lock(&conn->chan_lock);
+ c = __l2cap_get_chan_by_dcid(conn, cid);
+ if (c) {
+ /* Only lock if chan reference is not 0 */
+@@ -148,7 +145,6 @@ static struct l2cap_chan *l2cap_get_chan
+ if (c)
+ l2cap_chan_lock(c);
+ }
+- mutex_unlock(&conn->chan_lock);
+
+ return c;
+ }
+@@ -418,7 +414,7 @@ static void l2cap_chan_timeout(struct wo
+ if (!conn)
+ return;
+
+- mutex_lock(&conn->chan_lock);
++ mutex_lock(&conn->lock);
+ /* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling
+ * this work. No need to call l2cap_chan_hold(chan) here again.
+ */
+@@ -439,7 +435,7 @@ static void l2cap_chan_timeout(struct wo
+ l2cap_chan_unlock(chan);
+ l2cap_chan_put(chan);
+
+- mutex_unlock(&conn->chan_lock);
++ mutex_unlock(&conn->lock);
+ }
+
+ struct l2cap_chan *l2cap_chan_create(void)
+@@ -642,9 +638,9 @@ void __l2cap_chan_add(struct l2cap_conn
+
+ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
+ {
+- mutex_lock(&conn->chan_lock);
++ mutex_lock(&conn->lock);
+ __l2cap_chan_add(conn, chan);
+- mutex_unlock(&conn->chan_lock);
++ mutex_unlock(&conn->lock);
+ }
+
+ void l2cap_chan_del(struct l2cap_chan *chan, int err)
+@@ -732,9 +728,9 @@ void l2cap_chan_list(struct l2cap_conn *
+ if (!conn)
+ return;
+
+- mutex_lock(&conn->chan_lock);
++ mutex_lock(&conn->lock);
+ __l2cap_chan_list(conn, func, data);
+- mutex_unlock(&conn->chan_lock);
++ mutex_unlock(&conn->lock);
+ }
+
+ EXPORT_SYMBOL_GPL(l2cap_chan_list);
+@@ -746,7 +742,7 @@ static void l2cap_conn_update_id_addr(st
+ struct hci_conn *hcon = conn->hcon;
+ struct l2cap_chan *chan;
+
+- mutex_lock(&conn->chan_lock);
++ mutex_lock(&conn->lock);
+
+ list_for_each_entry(chan, &conn->chan_l, list) {
+ l2cap_chan_lock(chan);
+@@ -755,7 +751,7 @@ static void l2cap_conn_update_id_addr(st
+ l2cap_chan_unlock(chan);
+ }
+
+- mutex_unlock(&conn->chan_lock);
++ mutex_unlock(&conn->lock);
+ }
+
+ static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
+@@ -1508,8 +1504,6 @@ static void l2cap_conn_start(struct l2ca
+
+ BT_DBG("conn %p", conn);
+
+- mutex_lock(&conn->chan_lock);
+-
+ list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
+ l2cap_chan_lock(chan);
+
+@@ -1578,8 +1572,6 @@ static void l2cap_conn_start(struct l2ca
+
+ l2cap_chan_unlock(chan);
+ }
+-
+- mutex_unlock(&conn->chan_lock);
+ }
+
+ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
+@@ -1625,7 +1617,7 @@ static void l2cap_conn_ready(struct l2ca
+ if (hcon->type == ACL_LINK)
+ l2cap_request_info(conn);
+
+- mutex_lock(&conn->chan_lock);
++ mutex_lock(&conn->lock);
+
+ list_for_each_entry(chan, &conn->chan_l, list) {
+
+@@ -1643,7 +1635,7 @@ static void l2cap_conn_ready(struct l2ca
+ l2cap_chan_unlock(chan);
+ }
+
+- mutex_unlock(&conn->chan_lock);
++ mutex_unlock(&conn->lock);
+
+ if (hcon->type == LE_LINK)
+ l2cap_le_conn_ready(conn);
+@@ -1658,14 +1650,10 @@ static void l2cap_conn_unreliable(struct
+
+ BT_DBG("conn %p", conn);
+
+- mutex_lock(&conn->chan_lock);
+-
+ list_for_each_entry(chan, &conn->chan_l, list) {
+ if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
+ l2cap_chan_set_err(chan, err);
+ }
+-
+- mutex_unlock(&conn->chan_lock);
+ }
+
+ static void l2cap_info_timeout(struct work_struct *work)
+@@ -1676,7 +1664,9 @@ static void l2cap_info_timeout(struct wo
+ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+ conn->info_ident = 0;
+
++ mutex_lock(&conn->lock);
+ l2cap_conn_start(conn);
++ mutex_unlock(&conn->lock);
+ }
+
+ /*
+@@ -1768,6 +1758,8 @@ static void l2cap_conn_del(struct hci_co
+
+ BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
+
++ mutex_lock(&conn->lock);
++
+ kfree_skb(conn->rx_skb);
+
+ skb_queue_purge(&conn->pending_rx);
+@@ -1786,8 +1778,6 @@ static void l2cap_conn_del(struct hci_co
+ /* Force the connection to be immediately dropped */
+ hcon->disc_timeout = 0;
+
+- mutex_lock(&conn->chan_lock);
+-
+ /* Kill channels */
+ list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
+ l2cap_chan_hold(chan);
+@@ -1801,12 +1791,14 @@ static void l2cap_conn_del(struct hci_co
+ l2cap_chan_put(chan);
+ }
+
+- mutex_unlock(&conn->chan_lock);
+-
+ if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
+ cancel_delayed_work_sync(&conn->info_timer);
+
++ hci_chan_del(conn->hchan);
++ conn->hchan = NULL;
++
+ hcon->l2cap_data = NULL;
++ mutex_unlock(&conn->lock);
+ l2cap_conn_put(conn);
+ }
+
+@@ -1814,7 +1806,6 @@ static void l2cap_conn_free(struct kref
+ {
+ struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
+
+- hci_chan_del(conn->hchan);
+ hci_conn_put(conn->hcon);
+ kfree(conn);
+ }
+@@ -2925,8 +2916,6 @@ static void l2cap_raw_recv(struct l2cap_
+
+ BT_DBG("conn %p", conn);
+
+- mutex_lock(&conn->chan_lock);
+-
+ list_for_each_entry(chan, &conn->chan_l, list) {
+ if (chan->chan_type != L2CAP_CHAN_RAW)
+ continue;
+@@ -2941,8 +2930,6 @@ static void l2cap_raw_recv(struct l2cap_
+ if (chan->ops->recv(chan, nskb))
+ kfree_skb(nskb);
+ }
+-
+- mutex_unlock(&conn->chan_lock);
+ }
+
+ /* ---- L2CAP signalling commands ---- */
+@@ -3965,7 +3952,6 @@ static void l2cap_connect(struct l2cap_c
+ goto response;
+ }
+
+- mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(pchan);
+
+ /* Check if the ACL is secure enough (if not SDP) */
+@@ -4072,7 +4058,6 @@ response:
+ }
+
+ l2cap_chan_unlock(pchan);
+- mutex_unlock(&conn->chan_lock);
+ l2cap_chan_put(pchan);
+ }
+
+@@ -4111,27 +4096,19 @@ static int l2cap_connect_create_rsp(stru
+ BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x",
+ dcid, scid, result, status);
+
+- mutex_lock(&conn->chan_lock);
+-
+ if (scid) {
+ chan = __l2cap_get_chan_by_scid(conn, scid);
+- if (!chan) {
+- err = -EBADSLT;
+- goto unlock;
+- }
++ if (!chan)
++ return -EBADSLT;
+ } else {
+ chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+- if (!chan) {
+- err = -EBADSLT;
+- goto unlock;
+- }
++ if (!chan)
++ return -EBADSLT;
+ }
+
+ chan = l2cap_chan_hold_unless_zero(chan);
+- if (!chan) {
+- err = -EBADSLT;
+- goto unlock;
+- }
++ if (!chan)
++ return -EBADSLT;
+
+ err = 0;
+
+@@ -4169,9 +4146,6 @@ static int l2cap_connect_create_rsp(stru
+ l2cap_chan_unlock(chan);
+ l2cap_chan_put(chan);
+
+-unlock:
+- mutex_unlock(&conn->chan_lock);
+-
+ return err;
+ }
+
+@@ -4459,11 +4433,7 @@ static inline int l2cap_disconnect_req(s
+
+ chan->ops->set_shutdown(chan);
+
+- l2cap_chan_unlock(chan);
+- mutex_lock(&conn->chan_lock);
+- l2cap_chan_lock(chan);
+ l2cap_chan_del(chan, ECONNRESET);
+- mutex_unlock(&conn->chan_lock);
+
+ chan->ops->close(chan);
+
+@@ -4500,11 +4470,7 @@ static inline int l2cap_disconnect_rsp(s
+ return 0;
+ }
+
+- l2cap_chan_unlock(chan);
+- mutex_lock(&conn->chan_lock);
+- l2cap_chan_lock(chan);
+ l2cap_chan_del(chan, 0);
+- mutex_unlock(&conn->chan_lock);
+
+ chan->ops->close(chan);
+
+@@ -4702,13 +4668,9 @@ static int l2cap_le_connect_rsp(struct l
+ BT_DBG("dcid 0x%4.4x mtu %u mps %u credits %u result 0x%2.2x",
+ dcid, mtu, mps, credits, result);
+
+- mutex_lock(&conn->chan_lock);
+-
+ chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+- if (!chan) {
+- err = -EBADSLT;
+- goto unlock;
+- }
++ if (!chan)
++ return -EBADSLT;
+
+ err = 0;
+
+@@ -4756,9 +4718,6 @@ static int l2cap_le_connect_rsp(struct l
+
+ l2cap_chan_unlock(chan);
+
+-unlock:
+- mutex_unlock(&conn->chan_lock);
+-
+ return err;
+ }
+
+@@ -4870,7 +4829,6 @@ static int l2cap_le_connect_req(struct l
+ goto response;
+ }
+
+- mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(pchan);
+
+ if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
+@@ -4936,7 +4894,6 @@ static int l2cap_le_connect_req(struct l
+
+ response_unlock:
+ l2cap_chan_unlock(pchan);
+- mutex_unlock(&conn->chan_lock);
+ l2cap_chan_put(pchan);
+
+ if (result == L2CAP_CR_PEND)
+@@ -5070,7 +5027,6 @@ static inline int l2cap_ecred_conn_req(s
+ goto response;
+ }
+
+- mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(pchan);
+
+ if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
+@@ -5145,7 +5101,6 @@ static inline int l2cap_ecred_conn_req(s
+
+ unlock:
+ l2cap_chan_unlock(pchan);
+- mutex_unlock(&conn->chan_lock);
+ l2cap_chan_put(pchan);
+
+ response:
+@@ -5182,8 +5137,6 @@ static inline int l2cap_ecred_conn_rsp(s
+ BT_DBG("mtu %u mps %u credits %u result 0x%4.4x", mtu, mps, credits,
+ result);
+
+- mutex_lock(&conn->chan_lock);
+-
+ cmd_len -= sizeof(*rsp);
+
+ list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
+@@ -5269,8 +5222,6 @@ static inline int l2cap_ecred_conn_rsp(s
+ l2cap_chan_unlock(chan);
+ }
+
+- mutex_unlock(&conn->chan_lock);
+-
+ return err;
+ }
+
+@@ -5383,8 +5334,6 @@ static inline int l2cap_le_command_rej(s
+ if (cmd_len < sizeof(*rej))
+ return -EPROTO;
+
+- mutex_lock(&conn->chan_lock);
+-
+ chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+ if (!chan)
+ goto done;
+@@ -5399,7 +5348,6 @@ static inline int l2cap_le_command_rej(s
+ l2cap_chan_put(chan);
+
+ done:
+- mutex_unlock(&conn->chan_lock);
+ return 0;
+ }
+
+@@ -6854,8 +6802,12 @@ static void process_pending_rx(struct wo
+
+ BT_DBG("");
+
++ mutex_lock(&conn->lock);
++
+ while ((skb = skb_dequeue(&conn->pending_rx)))
+ l2cap_recv_frame(conn, skb);
++
++ mutex_unlock(&conn->lock);
+ }
+
+ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
+@@ -6894,7 +6846,7 @@ static struct l2cap_conn *l2cap_conn_add
+ conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR;
+
+ mutex_init(&conn->ident_lock);
+- mutex_init(&conn->chan_lock);
++ mutex_init(&conn->lock);
+
+ INIT_LIST_HEAD(&conn->chan_l);
+ INIT_LIST_HEAD(&conn->users);
+@@ -7085,7 +7037,7 @@ int l2cap_chan_connect(struct l2cap_chan
+ }
+ }
+
+- mutex_lock(&conn->chan_lock);
++ mutex_lock(&conn->lock);
+ l2cap_chan_lock(chan);
+
+ if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
+@@ -7126,7 +7078,7 @@ int l2cap_chan_connect(struct l2cap_chan
+
+ chan_unlock:
+ l2cap_chan_unlock(chan);
+- mutex_unlock(&conn->chan_lock);
++ mutex_unlock(&conn->lock);
+ done:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+@@ -7339,7 +7291,7 @@ static void l2cap_security_cfm(struct hc
+
+ BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt);
+
+- mutex_lock(&conn->chan_lock);
++ mutex_lock(&conn->lock);
+
+ list_for_each_entry(chan, &conn->chan_l, list) {
+ l2cap_chan_lock(chan);
+@@ -7413,7 +7365,7 @@ static void l2cap_security_cfm(struct hc
+ l2cap_chan_unlock(chan);
+ }
+
+- mutex_unlock(&conn->chan_lock);
++ mutex_unlock(&conn->lock);
+ }
+
+ /* Append fragment into frame respecting the maximum len of rx_skb */
+@@ -7480,8 +7432,11 @@ static void l2cap_recv_reset(struct l2ca
+ conn->rx_len = 0;
+ }
+
+-static struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c)
++struct l2cap_conn *l2cap_conn_hold_unless_zero(struct l2cap_conn *c)
+ {
++ if (!c)
++ return NULL;
++
+ BT_DBG("conn %p orig refcnt %u", c, kref_read(&c->ref));
+
+ if (!kref_get_unless_zero(&c->ref))
+@@ -7507,11 +7462,15 @@ void l2cap_recv_acldata(struct hci_conn
+
+ hci_dev_unlock(hcon->hdev);
+
+- if (!conn)
+- goto drop;
++ if (!conn) {
++ kfree_skb(skb);
++ return;
++ }
+
+ BT_DBG("conn %p len %u flags 0x%x", conn, skb->len, flags);
+
++ mutex_lock(&conn->lock);
++
+ switch (flags) {
+ case ACL_START:
+ case ACL_START_NO_FLUSH:
+@@ -7536,7 +7495,7 @@ void l2cap_recv_acldata(struct hci_conn
+ if (len == skb->len) {
+ /* Complete frame received */
+ l2cap_recv_frame(conn, skb);
+- return;
++ goto unlock;
+ }
+
+ BT_DBG("Start: total len %d, frag len %u", len, skb->len);
+@@ -7598,10 +7557,11 @@ void l2cap_recv_acldata(struct hci_conn
+ break;
+ }
+
+- l2cap_conn_put(conn);
+-
+ drop:
+ kfree_skb(skb);
++unlock:
++ mutex_unlock(&conn->lock);
++ l2cap_conn_put(conn);
+ }
+
+ static struct hci_cb l2cap_cb = {
+--- a/net/bluetooth/l2cap_sock.c
++++ b/net/bluetooth/l2cap_sock.c
+@@ -1326,9 +1326,10 @@ static int l2cap_sock_shutdown(struct so
+ /* prevent sk structure from being freed whilst unlocked */
+ sock_hold(sk);
+
+- chan = l2cap_pi(sk)->chan;
+ /* prevent chan structure from being freed whilst unlocked */
+- l2cap_chan_hold(chan);
++ chan = l2cap_chan_hold_unless_zero(l2cap_pi(sk)->chan);
++ if (!chan)
++ goto shutdown_already;
+
+ BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
+
+@@ -1358,22 +1359,20 @@ static int l2cap_sock_shutdown(struct so
+ release_sock(sk);
+
+ l2cap_chan_lock(chan);
+- conn = chan->conn;
+- if (conn)
+- /* prevent conn structure from being freed */
+- l2cap_conn_get(conn);
++ /* prevent conn structure from being freed */
++ conn = l2cap_conn_hold_unless_zero(chan->conn);
+ l2cap_chan_unlock(chan);
+
+ if (conn)
+ /* mutex lock must be taken before l2cap_chan_lock() */
+- mutex_lock(&conn->chan_lock);
++ mutex_lock(&conn->lock);
+
+ l2cap_chan_lock(chan);
+ l2cap_chan_close(chan, 0);
+ l2cap_chan_unlock(chan);
+
+ if (conn) {
+- mutex_unlock(&conn->chan_lock);
++ mutex_unlock(&conn->lock);
+ l2cap_conn_put(conn);
+ }
+
--- /dev/null
+From f13409bb3f9140dad7256febcb478f0c9600312c Mon Sep 17 00:00:00 2001
+From: Daniel Wagner <wagi@kernel.org>
+Date: Fri, 14 Feb 2025 09:02:04 +0100
+Subject: nvme-fc: rely on state transitions to handle connectivity loss
+
+From: Daniel Wagner <wagi@kernel.org>
+
+commit f13409bb3f9140dad7256febcb478f0c9600312c upstream.
+
+It's not possible to call nvme_state_ctrl_state with holding a spin
+lock, because nvme_state_ctrl_state calls cancel_delayed_work_sync
+when fastfail is enabled.
+
+Instead syncing the ASSOC_FLAG and state transitions using a lock, it's
+possible to only rely on the state machine transitions. That means
+nvme_fc_ctrl_connectivity_loss should unconditionally call
+nvme_reset_ctrl which avoids the read race on the ctrl state variable.
+Actually, it's not necessary to test in which state the ctrl is, the
+reset work will only scheduled when the state machine is in LIVE state.
+
+In nvme_fc_create_association, the LIVE state can only be entered if it
+was previously CONNECTING. If this is not possible then the reset
+handler got triggered. Thus just error out here.
+
+Fixes: ee59e3820ca9 ("nvme-fc: do not ignore connectivity loss during connecting")
+Closes: https://lore.kernel.org/all/denqwui6sl5erqmz2gvrwueyxakl5txzbbiu3fgebryzrfxunm@iwxuthct377m/
+Reported-by: Shinichiro Kawasaki <shinichiro.kawasaki@wdc.com>
+Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
+Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
+Signed-off-by: Daniel Wagner <wagi@kernel.org>
+Signed-off-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/nvme/host/fc.c | 67 ++++---------------------------------------------
+ 1 file changed, 6 insertions(+), 61 deletions(-)
+
+--- a/drivers/nvme/host/fc.c
++++ b/drivers/nvme/host/fc.c
+@@ -782,61 +782,12 @@ restart:
+ static void
+ nvme_fc_ctrl_connectivity_loss(struct nvme_fc_ctrl *ctrl)
+ {
+- enum nvme_ctrl_state state;
+- unsigned long flags;
+-
+ dev_info(ctrl->ctrl.device,
+ "NVME-FC{%d}: controller connectivity lost. Awaiting "
+ "Reconnect", ctrl->cnum);
+
+- spin_lock_irqsave(&ctrl->lock, flags);
+ set_bit(ASSOC_FAILED, &ctrl->flags);
+- state = nvme_ctrl_state(&ctrl->ctrl);
+- spin_unlock_irqrestore(&ctrl->lock, flags);
+-
+- switch (state) {
+- case NVME_CTRL_NEW:
+- case NVME_CTRL_LIVE:
+- /*
+- * Schedule a controller reset. The reset will terminate the
+- * association and schedule the reconnect timer. Reconnects
+- * will be attempted until either the ctlr_loss_tmo
+- * (max_retries * connect_delay) expires or the remoteport's
+- * dev_loss_tmo expires.
+- */
+- if (nvme_reset_ctrl(&ctrl->ctrl)) {
+- dev_warn(ctrl->ctrl.device,
+- "NVME-FC{%d}: Couldn't schedule reset.\n",
+- ctrl->cnum);
+- nvme_delete_ctrl(&ctrl->ctrl);
+- }
+- break;
+-
+- case NVME_CTRL_CONNECTING:
+- /*
+- * The association has already been terminated and the
+- * controller is attempting reconnects. No need to do anything
+- * futher. Reconnects will be attempted until either the
+- * ctlr_loss_tmo (max_retries * connect_delay) expires or the
+- * remoteport's dev_loss_tmo expires.
+- */
+- break;
+-
+- case NVME_CTRL_RESETTING:
+- /*
+- * Controller is already in the process of terminating the
+- * association. No need to do anything further. The reconnect
+- * step will kick in naturally after the association is
+- * terminated.
+- */
+- break;
+-
+- case NVME_CTRL_DELETING:
+- case NVME_CTRL_DELETING_NOIO:
+- default:
+- /* no action to take - let it delete */
+- break;
+- }
++ nvme_reset_ctrl(&ctrl->ctrl);
+ }
+
+ /**
+@@ -3072,7 +3023,6 @@ nvme_fc_create_association(struct nvme_f
+ struct nvmefc_ls_rcv_op *disls = NULL;
+ unsigned long flags;
+ int ret;
+- bool changed;
+
+ ++ctrl->ctrl.nr_reconnects;
+
+@@ -3178,23 +3128,18 @@ nvme_fc_create_association(struct nvme_f
+ else
+ ret = nvme_fc_recreate_io_queues(ctrl);
+ }
++ if (!ret && test_bit(ASSOC_FAILED, &ctrl->flags))
++ ret = -EIO;
+ if (ret)
+ goto out_term_aen_ops;
+
+- spin_lock_irqsave(&ctrl->lock, flags);
+- if (!test_bit(ASSOC_FAILED, &ctrl->flags))
+- changed = nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_LIVE);
+- else
++ if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_LIVE)) {
+ ret = -EIO;
+- spin_unlock_irqrestore(&ctrl->lock, flags);
+-
+- if (ret)
+ goto out_term_aen_ops;
++ }
+
+ ctrl->ctrl.nr_reconnects = 0;
+-
+- if (changed)
+- nvme_start_ctrl(&ctrl->ctrl);
++ nvme_start_ctrl(&ctrl->ctrl);
+
+ return 0; /* Success */
+