[PATCH] Bluetooth: SCO: Fix use-after-free on listening socket in sco_conn_ready()

From: Sanghyun Park

Date: Fri May 29 2026 - 03:16:15 EST


sco_conn_ready() calls sco_get_sock_listen() which returns a raw
pointer to a listening socket after releasing sco_sk_list.lock, without
taking a reference. A concurrent close() of the listening socket can
free it between the list lookup return and lock_sock(parent), resulting
in a use-after-free.

Fix by taking a reference with sock_hold() immediately after
sco_get_sock_listen() returns, and dropping it with sock_put() after
release_sock(). This matches the pattern used in commit 598dbba9919c
("Bluetooth: SCO: Fix use-after-free in sco_recv_frame() due to missing
sock_hold") for the analogous race in sco_recv_frame().

Race:

  CPU0 (HCI event workqueue)         CPU1 (userspace)
  ============================       ==========================
  sco_conn_ready():
    parent = sco_get_sock_listen()
      // returns sk with NO reference
                                     close(listen_fd):
                                       sco_sock_release()
                                         sco_sock_kill()
                                           sock_put(sk) -> frees sk
    lock_sock(parent)
    // UAF: sk is freed

Reproduction:

  1. Build any kernel (bug exists since 2.6.12) with CONFIG_KASAN=y,
     CONFIG_BT=y, CONFIG_BT_HCIVHCI=m
  2. Boot in a VM, load hci_vhci module
  3. Compile: gcc -O2 -o repro -static -pthread repro.c
  4. Run as root: ./repro
  5. Check dmesg for: BUG: KASAN: slab-use-after-free in __lock_acquire

  The reproducer opens /dev/vhci, brings up a virtual HCI device,
  creates a SCO listening socket, then races close(listen_fd) against
  injected incoming SCO connection events. A 5ms instrumentation delay
  at the vulnerable point widens the window for reliable reproduction;
  without it the race is tight but still real on multi-core systems.

KASAN report (reproduced on 6.12.91 via /dev/vhci):

  BUG: KASAN: slab-use-after-free in __lock_acquire+0x2e19/0x3b50
  Read of size 8 at addr ffff888104be5258 by task kworker/u9:0/382

  Workqueue: hci0 hci_rx_work
  Call Trace:
   __lock_acquire+0x2e19/0x3b50
   lock_acquire.part.0+0xf7/0x320
   lock_sock_nested+0x46/0x100
   sco_connect_cfm.cold+0x2e7/0x867
   hci_connect_cfm+0x94/0x140
   hci_conn_complete_evt+0x825/0x13d0

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Sanghyun Park <sanghyun.park.cnu@xxxxxxxxx>
---

Hi,

I'm Sanghyun Park, a security researcher. I found this while auditing
the Bluetooth SCO code. The bug has existed since the initial git import
(2005) and affects literally every Linux kernel ever shipped. All distros
are affected.

The C reproducer is attached separately (repro.c).

 net/bluetooth/sco.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index ad3439bd4d..b5c6d7e8f1 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -1323,6 +1323,7 @@ static void sco_conn_ready(struct sco_conn *conn)
  sco_conn_unlock(conn);
  return;
  }
+ sock_hold(parent);
 
  lock_sock(parent);
 
@@ -1330,6 +1331,7 @@ static void sco_conn_ready(struct sco_conn *conn)
     BTPROTO_SCO, GFP_ATOMIC, 0);
  if (!sk) {
  release_sock(parent);
+ sock_put(parent);
  sco_conn_unlock(conn);
  return;
  }
@@ -1353,6 +1355,7 @@ static void sco_conn_ready(struct sco_conn *conn)
  parent->sk_data_ready(parent);
 
  release_sock(parent);
+ sock_put(parent);
 
  sco_conn_unlock(conn);
  }

Attachment: repro.c
Description: Binary data