1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

Bluetooth: L2CAP: Fix corrupted list in hci_chan_del

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: b4f82f9ed4 ("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>
This commit is contained in:
Luiz Augusto von Dentz 2025-02-06 15:54:45 -05:00
parent 872274b992
commit ab4eedb790
3 changed files with 58 additions and 98 deletions

View file

@ -668,7 +668,7 @@ struct l2cap_conn {
struct l2cap_chan *smp; struct l2cap_chan *smp;
struct list_head chan_l; struct list_head chan_l;
struct mutex chan_lock; struct mutex lock;
struct kref ref; struct kref ref;
struct list_head users; struct list_head users;
}; };
@ -970,6 +970,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err);
void l2cap_send_conn_req(struct l2cap_chan *chan); void l2cap_send_conn_req(struct l2cap_chan *chan);
struct l2cap_conn *l2cap_conn_get(struct l2cap_conn *conn); 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); void l2cap_conn_put(struct l2cap_conn *conn);
int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user); int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user);

View file

@ -119,7 +119,6 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
{ {
struct l2cap_chan *c; struct l2cap_chan *c;
mutex_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_scid(conn, cid); c = __l2cap_get_chan_by_scid(conn, cid);
if (c) { if (c) {
/* Only lock if chan reference is not 0 */ /* Only lock if chan reference is not 0 */
@ -127,7 +126,6 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
if (c) if (c)
l2cap_chan_lock(c); l2cap_chan_lock(c);
} }
mutex_unlock(&conn->chan_lock);
return c; return c;
} }
@ -140,7 +138,6 @@ static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
{ {
struct l2cap_chan *c; struct l2cap_chan *c;
mutex_lock(&conn->chan_lock);
c = __l2cap_get_chan_by_dcid(conn, cid); c = __l2cap_get_chan_by_dcid(conn, cid);
if (c) { if (c) {
/* Only lock if chan reference is not 0 */ /* Only lock if chan reference is not 0 */
@ -148,7 +145,6 @@ static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
if (c) if (c)
l2cap_chan_lock(c); l2cap_chan_lock(c);
} }
mutex_unlock(&conn->chan_lock);
return c; return c;
} }
@ -418,7 +414,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
if (!conn) if (!conn)
return; return;
mutex_lock(&conn->chan_lock); mutex_lock(&conn->lock);
/* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling /* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling
* this work. No need to call l2cap_chan_hold(chan) here again. * this work. No need to call l2cap_chan_hold(chan) here again.
*/ */
@ -439,7 +435,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
l2cap_chan_put(chan); l2cap_chan_put(chan);
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->lock);
} }
struct l2cap_chan *l2cap_chan_create(void) struct l2cap_chan *l2cap_chan_create(void)
@ -641,9 +637,9 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) 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); __l2cap_chan_add(conn, chan);
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->lock);
} }
void l2cap_chan_del(struct l2cap_chan *chan, int err) void l2cap_chan_del(struct l2cap_chan *chan, int err)
@ -731,9 +727,9 @@ void l2cap_chan_list(struct l2cap_conn *conn, l2cap_chan_func_t func,
if (!conn) if (!conn)
return; return;
mutex_lock(&conn->chan_lock); mutex_lock(&conn->lock);
__l2cap_chan_list(conn, func, data); __l2cap_chan_list(conn, func, data);
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->lock);
} }
EXPORT_SYMBOL_GPL(l2cap_chan_list); EXPORT_SYMBOL_GPL(l2cap_chan_list);
@ -745,7 +741,7 @@ static void l2cap_conn_update_id_addr(struct work_struct *work)
struct hci_conn *hcon = conn->hcon; struct hci_conn *hcon = conn->hcon;
struct l2cap_chan *chan; struct l2cap_chan *chan;
mutex_lock(&conn->chan_lock); mutex_lock(&conn->lock);
list_for_each_entry(chan, &conn->chan_l, list) { list_for_each_entry(chan, &conn->chan_l, list) {
l2cap_chan_lock(chan); l2cap_chan_lock(chan);
@ -754,7 +750,7 @@ static void l2cap_conn_update_id_addr(struct work_struct *work)
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
} }
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->lock);
} }
static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan) static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
@ -1507,8 +1503,6 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
BT_DBG("conn %p", conn); BT_DBG("conn %p", conn);
mutex_lock(&conn->chan_lock);
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
l2cap_chan_lock(chan); l2cap_chan_lock(chan);
@ -1577,8 +1571,6 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
} }
mutex_unlock(&conn->chan_lock);
} }
static void l2cap_le_conn_ready(struct l2cap_conn *conn) static void l2cap_le_conn_ready(struct l2cap_conn *conn)
@ -1624,7 +1616,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
if (hcon->type == ACL_LINK) if (hcon->type == ACL_LINK)
l2cap_request_info(conn); l2cap_request_info(conn);
mutex_lock(&conn->chan_lock); mutex_lock(&conn->lock);
list_for_each_entry(chan, &conn->chan_l, list) { list_for_each_entry(chan, &conn->chan_l, list) {
@ -1642,7 +1634,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
} }
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->lock);
if (hcon->type == LE_LINK) if (hcon->type == LE_LINK)
l2cap_le_conn_ready(conn); l2cap_le_conn_ready(conn);
@ -1657,14 +1649,10 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
BT_DBG("conn %p", conn); BT_DBG("conn %p", conn);
mutex_lock(&conn->chan_lock);
list_for_each_entry(chan, &conn->chan_l, list) { list_for_each_entry(chan, &conn->chan_l, list) {
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
l2cap_chan_set_err(chan, err); l2cap_chan_set_err(chan, err);
} }
mutex_unlock(&conn->chan_lock);
} }
static void l2cap_info_timeout(struct work_struct *work) static void l2cap_info_timeout(struct work_struct *work)
@ -1675,7 +1663,9 @@ static void l2cap_info_timeout(struct work_struct *work)
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0; conn->info_ident = 0;
mutex_lock(&conn->lock);
l2cap_conn_start(conn); l2cap_conn_start(conn);
mutex_unlock(&conn->lock);
} }
/* /*
@ -1767,6 +1757,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
mutex_lock(&conn->lock);
kfree_skb(conn->rx_skb); kfree_skb(conn->rx_skb);
skb_queue_purge(&conn->pending_rx); skb_queue_purge(&conn->pending_rx);
@ -1785,8 +1777,6 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
/* Force the connection to be immediately dropped */ /* Force the connection to be immediately dropped */
hcon->disc_timeout = 0; hcon->disc_timeout = 0;
mutex_lock(&conn->chan_lock);
/* Kill channels */ /* Kill channels */
list_for_each_entry_safe(chan, l, &conn->chan_l, list) { list_for_each_entry_safe(chan, l, &conn->chan_l, list) {
l2cap_chan_hold(chan); l2cap_chan_hold(chan);
@ -1800,12 +1790,14 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
l2cap_chan_put(chan); l2cap_chan_put(chan);
} }
mutex_unlock(&conn->chan_lock);
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
cancel_delayed_work_sync(&conn->info_timer); cancel_delayed_work_sync(&conn->info_timer);
hci_chan_del(conn->hchan);
conn->hchan = NULL;
hcon->l2cap_data = NULL; hcon->l2cap_data = NULL;
mutex_unlock(&conn->lock);
l2cap_conn_put(conn); l2cap_conn_put(conn);
} }
@ -1813,7 +1805,6 @@ static void l2cap_conn_free(struct kref *ref)
{ {
struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref); struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
hci_chan_del(conn->hchan);
hci_conn_put(conn->hcon); hci_conn_put(conn->hcon);
kfree(conn); kfree(conn);
} }
@ -2924,8 +2915,6 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("conn %p", conn); BT_DBG("conn %p", conn);
mutex_lock(&conn->chan_lock);
list_for_each_entry(chan, &conn->chan_l, list) { list_for_each_entry(chan, &conn->chan_l, list) {
if (chan->chan_type != L2CAP_CHAN_RAW) if (chan->chan_type != L2CAP_CHAN_RAW)
continue; continue;
@ -2940,8 +2929,6 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
if (chan->ops->recv(chan, nskb)) if (chan->ops->recv(chan, nskb))
kfree_skb(nskb); kfree_skb(nskb);
} }
mutex_unlock(&conn->chan_lock);
} }
/* ---- L2CAP signalling commands ---- */ /* ---- L2CAP signalling commands ---- */
@ -3960,7 +3947,6 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
goto response; goto response;
} }
mutex_lock(&conn->chan_lock);
l2cap_chan_lock(pchan); l2cap_chan_lock(pchan);
/* Check if the ACL is secure enough (if not SDP) */ /* Check if the ACL is secure enough (if not SDP) */
@ -4067,7 +4053,6 @@ response:
} }
l2cap_chan_unlock(pchan); l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
l2cap_chan_put(pchan); l2cap_chan_put(pchan);
} }
@ -4106,27 +4091,19 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x",
dcid, scid, result, status); dcid, scid, result, status);
mutex_lock(&conn->chan_lock);
if (scid) { if (scid) {
chan = __l2cap_get_chan_by_scid(conn, scid); chan = __l2cap_get_chan_by_scid(conn, scid);
if (!chan) { if (!chan)
err = -EBADSLT; return -EBADSLT;
goto unlock;
}
} else { } else {
chan = __l2cap_get_chan_by_ident(conn, cmd->ident); chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
if (!chan) { if (!chan)
err = -EBADSLT; return -EBADSLT;
goto unlock;
}
} }
chan = l2cap_chan_hold_unless_zero(chan); chan = l2cap_chan_hold_unless_zero(chan);
if (!chan) { if (!chan)
err = -EBADSLT; return -EBADSLT;
goto unlock;
}
err = 0; err = 0;
@ -4164,9 +4141,6 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
l2cap_chan_put(chan); l2cap_chan_put(chan);
unlock:
mutex_unlock(&conn->chan_lock);
return err; return err;
} }
@ -4454,11 +4428,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
chan->ops->set_shutdown(chan); chan->ops->set_shutdown(chan);
l2cap_chan_unlock(chan);
mutex_lock(&conn->chan_lock);
l2cap_chan_lock(chan);
l2cap_chan_del(chan, ECONNRESET); l2cap_chan_del(chan, ECONNRESET);
mutex_unlock(&conn->chan_lock);
chan->ops->close(chan); chan->ops->close(chan);
@ -4495,11 +4465,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
return 0; return 0;
} }
l2cap_chan_unlock(chan);
mutex_lock(&conn->chan_lock);
l2cap_chan_lock(chan);
l2cap_chan_del(chan, 0); l2cap_chan_del(chan, 0);
mutex_unlock(&conn->chan_lock);
chan->ops->close(chan); chan->ops->close(chan);
@ -4697,13 +4663,9 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
BT_DBG("dcid 0x%4.4x mtu %u mps %u credits %u result 0x%2.2x", BT_DBG("dcid 0x%4.4x mtu %u mps %u credits %u result 0x%2.2x",
dcid, mtu, mps, credits, result); dcid, mtu, mps, credits, result);
mutex_lock(&conn->chan_lock);
chan = __l2cap_get_chan_by_ident(conn, cmd->ident); chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
if (!chan) { if (!chan)
err = -EBADSLT; return -EBADSLT;
goto unlock;
}
err = 0; err = 0;
@ -4751,9 +4713,6 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
unlock:
mutex_unlock(&conn->chan_lock);
return err; return err;
} }
@ -4865,7 +4824,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
goto response; goto response;
} }
mutex_lock(&conn->chan_lock);
l2cap_chan_lock(pchan); l2cap_chan_lock(pchan);
if (!smp_sufficient_security(conn->hcon, pchan->sec_level, if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
@ -4931,7 +4889,6 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
response_unlock: response_unlock:
l2cap_chan_unlock(pchan); l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
l2cap_chan_put(pchan); l2cap_chan_put(pchan);
if (result == L2CAP_CR_PEND) if (result == L2CAP_CR_PEND)
@ -5065,7 +5022,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
goto response; goto response;
} }
mutex_lock(&conn->chan_lock);
l2cap_chan_lock(pchan); l2cap_chan_lock(pchan);
if (!smp_sufficient_security(conn->hcon, pchan->sec_level, if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
@ -5140,7 +5096,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
unlock: unlock:
l2cap_chan_unlock(pchan); l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
l2cap_chan_put(pchan); l2cap_chan_put(pchan);
response: response:
@ -5177,8 +5132,6 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
BT_DBG("mtu %u mps %u credits %u result 0x%4.4x", mtu, mps, credits, BT_DBG("mtu %u mps %u credits %u result 0x%4.4x", mtu, mps, credits,
result); result);
mutex_lock(&conn->chan_lock);
cmd_len -= sizeof(*rsp); cmd_len -= sizeof(*rsp);
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
@ -5264,8 +5217,6 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
} }
mutex_unlock(&conn->chan_lock);
return err; return err;
} }
@ -5378,8 +5329,6 @@ static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
if (cmd_len < sizeof(*rej)) if (cmd_len < sizeof(*rej))
return -EPROTO; return -EPROTO;
mutex_lock(&conn->chan_lock);
chan = __l2cap_get_chan_by_ident(conn, cmd->ident); chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
if (!chan) if (!chan)
goto done; goto done;
@ -5394,7 +5343,6 @@ static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
l2cap_chan_put(chan); l2cap_chan_put(chan);
done: done:
mutex_unlock(&conn->chan_lock);
return 0; return 0;
} }
@ -6849,8 +6797,12 @@ static void process_pending_rx(struct work_struct *work)
BT_DBG(""); BT_DBG("");
mutex_lock(&conn->lock);
while ((skb = skb_dequeue(&conn->pending_rx))) while ((skb = skb_dequeue(&conn->pending_rx)))
l2cap_recv_frame(conn, skb); l2cap_recv_frame(conn, skb);
mutex_unlock(&conn->lock);
} }
static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
@ -6889,7 +6841,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR; conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR;
mutex_init(&conn->ident_lock); mutex_init(&conn->ident_lock);
mutex_init(&conn->chan_lock); mutex_init(&conn->lock);
INIT_LIST_HEAD(&conn->chan_l); INIT_LIST_HEAD(&conn->chan_l);
INIT_LIST_HEAD(&conn->users); INIT_LIST_HEAD(&conn->users);
@ -7080,7 +7032,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
} }
} }
mutex_lock(&conn->chan_lock); mutex_lock(&conn->lock);
l2cap_chan_lock(chan); l2cap_chan_lock(chan);
if (cid && __l2cap_get_chan_by_dcid(conn, cid)) { if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
@ -7121,7 +7073,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
chan_unlock: chan_unlock:
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->lock);
done: done:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
@ -7333,7 +7285,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt); 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) { list_for_each_entry(chan, &conn->chan_l, list) {
l2cap_chan_lock(chan); l2cap_chan_lock(chan);
@ -7407,7 +7359,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
} }
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->lock);
} }
/* Append fragment into frame respecting the maximum len of rx_skb */ /* Append fragment into frame respecting the maximum len of rx_skb */
@ -7474,8 +7426,11 @@ static void l2cap_recv_reset(struct l2cap_conn *conn)
conn->rx_len = 0; 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)); BT_DBG("conn %p orig refcnt %u", c, kref_read(&c->ref));
if (!kref_get_unless_zero(&c->ref)) if (!kref_get_unless_zero(&c->ref))
@ -7501,11 +7456,15 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
hci_dev_unlock(hcon->hdev); hci_dev_unlock(hcon->hdev);
if (!conn) if (!conn) {
goto drop; kfree_skb(skb);
return;
}
BT_DBG("conn %p len %u flags 0x%x", conn, skb->len, flags); BT_DBG("conn %p len %u flags 0x%x", conn, skb->len, flags);
mutex_lock(&conn->lock);
switch (flags) { switch (flags) {
case ACL_START: case ACL_START:
case ACL_START_NO_FLUSH: case ACL_START_NO_FLUSH:
@ -7530,7 +7489,7 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
if (len == skb->len) { if (len == skb->len) {
/* Complete frame received */ /* Complete frame received */
l2cap_recv_frame(conn, skb); l2cap_recv_frame(conn, skb);
return; goto unlock;
} }
BT_DBG("Start: total len %d, frag len %u", len, skb->len); BT_DBG("Start: total len %d, frag len %u", len, skb->len);
@ -7592,10 +7551,11 @@ void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
break; break;
} }
l2cap_conn_put(conn);
drop: drop:
kfree_skb(skb); kfree_skb(skb);
unlock:
mutex_unlock(&conn->lock);
l2cap_conn_put(conn);
} }
static struct hci_cb l2cap_cb = { static struct hci_cb l2cap_cb = {

View file

@ -1326,9 +1326,10 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
/* prevent sk structure from being freed whilst unlocked */ /* prevent sk structure from being freed whilst unlocked */
sock_hold(sk); sock_hold(sk);
chan = l2cap_pi(sk)->chan;
/* prevent chan structure from being freed whilst unlocked */ /* 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)); BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
@ -1358,22 +1359,20 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
release_sock(sk); release_sock(sk);
l2cap_chan_lock(chan); l2cap_chan_lock(chan);
conn = chan->conn; /* prevent conn structure from being freed */
if (conn) conn = l2cap_conn_hold_unless_zero(chan->conn);
/* prevent conn structure from being freed */
l2cap_conn_get(conn);
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
if (conn) if (conn)
/* mutex lock must be taken before l2cap_chan_lock() */ /* mutex lock must be taken before l2cap_chan_lock() */
mutex_lock(&conn->chan_lock); mutex_lock(&conn->lock);
l2cap_chan_lock(chan); l2cap_chan_lock(chan);
l2cap_chan_close(chan, 0); l2cap_chan_close(chan, 0);
l2cap_chan_unlock(chan); l2cap_chan_unlock(chan);
if (conn) { if (conn) {
mutex_unlock(&conn->chan_lock); mutex_unlock(&conn->lock);
l2cap_conn_put(conn); l2cap_conn_put(conn);
} }