Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Bluetooth: Fix locking in bt_accept_enqueue() for BH context

With commit e16337622016 ("Bluetooth: Handle bt_accept_enqueue() socket
atomically") lock_sock[_nested]() is used to acquire the socket lock
before manipulating the socket. lock_sock[_nested]() may block, which
is problematic since bt_accept_enqueue() can be called in bottom half
context (e.g. from rfcomm_connect_ind()):

[<ffffff80080d81ec>] __might_sleep+0x4c/0x80
[<ffffff800876c7b0>] lock_sock_nested+0x24/0x58
[<ffffff8000d7c27c>] bt_accept_enqueue+0x48/0xd4 [bluetooth]
[<ffffff8000e67d8c>] rfcomm_connect_ind+0x190/0x218 [rfcomm]

Add a parameter to bt_accept_enqueue() to indicate whether the
function is called from BH context, and acquire the socket lock
with bh_lock_sock_nested() if that's the case.

Also adapt all callers of bt_accept_enqueue() to pass the new
parameter:

- l2cap_sock_new_connection_cb()
- uses lock_sock() to lock the parent socket => process context

- rfcomm_connect_ind()
- acquires the parent socket lock with bh_lock_sock() => BH
context

- __sco_chan_add()
- called from sco_chan_add(), which is called from sco_connect().
parent is NULL, hence bt_accept_enqueue() isn't called in this
code path and we can ignore it
- also called from sco_conn_ready(). uses bh_lock_sock() to acquire
the parent lock => BH context

Fixes: e16337622016 ("Bluetooth: Handle bt_accept_enqueue() socket atomically")
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Cc: stable@vger.kernel.org

authored by

Matthias Kaehlcke and committed by
Marcel Holtmann
c4f5627f 8c57983b

+17 -7
+1 -1
include/net/bluetooth/bluetooth.h
··· 276 276 int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo); 277 277 int bt_sock_wait_ready(struct sock *sk, unsigned long flags); 278 278 279 - void bt_accept_enqueue(struct sock *parent, struct sock *sk); 279 + void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh); 280 280 void bt_accept_unlink(struct sock *sk); 281 281 struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock); 282 282
+13 -3
net/bluetooth/af_bluetooth.c
··· 154 154 } 155 155 EXPORT_SYMBOL(bt_sock_unlink); 156 156 157 - void bt_accept_enqueue(struct sock *parent, struct sock *sk) 157 + void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh) 158 158 { 159 159 BT_DBG("parent %p, sk %p", parent, sk); 160 160 161 161 sock_hold(sk); 162 - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); 162 + 163 + if (bh) 164 + bh_lock_sock_nested(sk); 165 + else 166 + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); 167 + 163 168 list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q); 164 169 bt_sk(sk)->parent = parent; 165 - release_sock(sk); 170 + 171 + if (bh) 172 + bh_unlock_sock(sk); 173 + else 174 + release_sock(sk); 175 + 166 176 parent->sk_ack_backlog++; 167 177 } 168 178 EXPORT_SYMBOL(bt_accept_enqueue);
+1 -1
net/bluetooth/l2cap_sock.c
··· 1252 1252 1253 1253 l2cap_sock_init(sk, parent); 1254 1254 1255 - bt_accept_enqueue(parent, sk); 1255 + bt_accept_enqueue(parent, sk, false); 1256 1256 1257 1257 release_sock(parent); 1258 1258
+1 -1
net/bluetooth/rfcomm/sock.c
··· 988 988 rfcomm_pi(sk)->channel = channel; 989 989 990 990 sk->sk_state = BT_CONFIG; 991 - bt_accept_enqueue(parent, sk); 991 + bt_accept_enqueue(parent, sk, true); 992 992 993 993 /* Accept connection and return socket DLC */ 994 994 *d = rfcomm_pi(sk)->dlc;
+1 -1
net/bluetooth/sco.c
··· 193 193 conn->sk = sk; 194 194 195 195 if (parent) 196 - bt_accept_enqueue(parent, sk); 196 + bt_accept_enqueue(parent, sk, true); 197 197 } 198 198 199 199 static int sco_chan_add(struct sco_conn *conn, struct sock *sk,