Skip to content

Commit c66a95e

Browse files
Yousef13710gregkh
authored andcommitted
Bluetooth: fix UAF in bt_accept_dequeue()
commit 4bd0b27 upstream. bt_accept_get() takes a temporary reference before dropping the accept queue lock. bt_accept_dequeue() currently drops that reference before bt_accept_unlink(), leaving only the queue reference. bt_accept_unlink() drops the queue reference. The subsequent sock_hold() therefore accesses freed memory if it was the final reference, as observed by KASAN during listening L2CAP socket cleanup. Retain the temporary queue-walk reference through unlink and hand it to the caller on success. Drop it explicitly on the closed and not-yet-connected paths. Fixes: ab15135 ("Bluetooth: fix UAF in l2cap_sock_cleanup_listen() vs l2cap_conn_del()") Reported-by: syzbot+674ff7e4d7fdfd572afc@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=674ff7e4d7fdfd572afc Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen <alhouseenyousef@gmail.com> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 21e60eb commit c66a95e

2 files changed

Lines changed: 5 additions & 16 deletions

File tree

net/bluetooth/af_bluetooth.c

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
305305

306306
restart:
307307
for (sk = bt_accept_get(parent, NULL); sk; sk = next) {
308-
/* Prevent early freeing of sk due to unlink and sock_kill */
308+
/* The reference from bt_accept_get() keeps sk alive. */
309309
lock_sock(sk);
310310

311311
/* Check sk has not already been unlinked via
@@ -321,13 +321,11 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
321321

322322
next = bt_accept_get(parent, sk);
323323

324-
/* sk is safely in the parent list so reduce reference count */
325-
sock_put(sk);
326-
327324
/* FIXME: Is this check still needed */
328325
if (sk->sk_state == BT_CLOSED) {
329326
bt_accept_unlink(sk);
330327
release_sock(sk);
328+
sock_put(sk);
331329
continue;
332330
}
333331

@@ -337,23 +335,14 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
337335
if (newsock)
338336
sock_graft(sk, newsock);
339337

340-
/* Hand the caller a reference taken while sk is
341-
* still locked. bt_accept_unlink() just dropped
342-
* the accept-queue reference; without this hold a
343-
* concurrent teardown (e.g. l2cap_conn_del() ->
344-
* l2cap_sock_kill()) could free sk between
345-
* release_sock() and the caller using it. Every
346-
* caller drops this with sock_put() when done.
347-
*/
348-
sock_hold(sk);
349-
350338
release_sock(sk);
351339
if (next)
352340
sock_put(next);
353341
return sk;
354342
}
355343

356344
release_sock(sk);
345+
sock_put(sk);
357346
}
358347

359348
return NULL;

net/bluetooth/l2cap_sock.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,8 +1448,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
14481448

14491449
/* Close not yet accepted channels.
14501450
*
1451-
* bt_accept_dequeue() now returns sk with an extra reference held
1452-
* (taken while sk was still locked) so a concurrent l2cap_conn_del()
1451+
* bt_accept_dequeue() returns sk with its temporary queue-walk
1452+
* reference held, so a concurrent l2cap_conn_del()
14531453
* -> l2cap_sock_kill() cannot free sk under us.
14541454
*
14551455
* cleanup_listen() runs under the parent sk lock, so unlike

0 commit comments

Comments
 (0)