CVE-2026-80683: Bluetooth: SCO: give the socket its own sco_conn reference
In the Linux kernel, the following vulnerability has been resolved:
Bluetooth: SCO: give the socket its own scoconn reference
scoconndel() drops a reference it does not own. It takes one transient reference via scoconnholdunlesszero() and releases it with the scoconnput() that follows scosockhold(); the additional put in the !sk branch releases a second one:
conn = scoconnholdunlesszero(conn); ... sk = scosockhold(conn); scoconnunlock(conn); scoconnput(conn);
if (!sk) { scoconnput(conn); return; }
When close() races the controller's Disconnection Complete, scochandel() clears conn->sk and drops the socket's reference while scoconndel() is running. scoconndel() then sees sk == NULL, its own put drops the count to zero and frees the conn, and the second put writes to the freed kref:
BUG: KASAN: slab-use-after-free in scoconnput.part.0+0x1a/0x190 Write of size 4 at addr ffff8881099dec74 by task kworker/u17:3/413 Workqueue: hci1 hcirxwork Call Trace: scoconnput.part.0+0x1a/0x190 hcidisconncompleteevt+0x1ee/0x3e0 hcieventpacket+0x54a/0x650 hcirxwork+0x321/0x3d0 Allocated by task 413: scoconnadd+0x72/0x1a0 scoconnectcfm+0x88/0x670 Freed by task 413: scoconndel.isra.0+0x3f/0xf0 hcidisconncompleteevt+0x1ee/0x3e0 refcountt: underflow; use-after-free.
The root cause is that the socket stores the connection without holding a reference of its own. scochanadd() does:
scopi(sk)->conn = conn;
so the socket borrows whatever reference its caller happened to hold, and the callers paper over that with ad-hoc holds and puts. Give the socket a counted reference instead: scochanadd() takes one and it is released together with the channel (scochandel()) and in scosockdestruct(). With the socket holding its own reference, scoconndel() no longer needs the extra put and the redundant hold in scoconnready() goes away.
Making the socket own its reference means the connection is now actually freed on the error paths of scoconnect() where it used to leak, which in turn runs scoconnfree() and its hciconndrop(conn->hcon). To keep the hciconn accounting balanced, make that ownership explicit as well: scoconnadd() consumes one hciconn reference and the scoconn owns it for its lifetime. scoconnect() hands over the reference returned by hciconnectsco() and no longer drops it on the error paths; scoconnectcfm(), which is not given a reference, takes one with hciconnhold() before handing it to scoconnadd() (and drops it again if the allocation fails); and the explicit hciconnhold() in scoconnready() is removed. Every reference then has a single, clear owner.