From nobody Fri Oct 2 10:08:42 2026 Received: from meesny.iki.fi (meesny.iki.fi [195.140.195.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 654A83B1029; Sun, 2 Aug 2026 12:14:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=195.140.195.201 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785672862; cv=pass; b=dO/C+zilAF9EJJ+JC0TiR7iTJXfzEhvdT5ddkmevCP7hA5FpsbKk/s9RN3ZKsOzByUozOHwjWpHqkrKfQND9lQP//ev9WNHO20Bdp1hc2dZ2+Mzw1w769gZP4Nx26lb+EbQnyox8MLLXYShClOPhPjtXBnhNpB0yxqDIKn3Av7A= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785672862; c=relaxed/simple; bh=0afh3xMe5azLu7BwSe/G8274JtObho3dCqxukXUFh+I=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=c6qdvlcRCZkYLEtIwljGEjQwUAeFiVUffLTFfr1NK7Uo8H/Rw/Bh5gu0Eoi93vCVbaq3CoDJP3lz4Flni6Q/3v+f5OfesHFW3pMxUZjNnuoQgQU0jVFn6bn9uwox6BGXUrvxLp369oL3UN0hNfmnS8czvYI/aJql5O795AcdQDE= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=iki.fi; spf=pass smtp.mailfrom=iki.fi; dkim=pass (1024-bit key) header.d=iki.fi header.i=@iki.fi header.b=JpazydUJ; arc=pass smtp.client-ip=195.140.195.201 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=iki.fi Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=iki.fi Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=iki.fi header.i=@iki.fi header.b="JpazydUJ" Received: from monolith.lan (unknown [IPv6:2a02:6ea0:1508:4::d001]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: pav) by meesny.iki.fi (Postfix) with ESMTPSA id 4hCdyX685jzyQZ; Sun, 02 Aug 2026 15:14:12 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=meesny; t=1785672853; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=HpeiDNvEVOrg91qVprA1oDDCAq7S/nXBH9j1ifmqiIc=; b=JpazydUJ6ds4DAvRzOZXesYr0TVQ0Sg3upeK9Gj9j+K36zUqkn+pklaKst2cGIQfJ+B4lE FJz/3tF/56Wg1JpWd8OLIGpDPbVOtBKs8faoZu3tKyed5Kxq1WVzO5yWICcHlPTpoqG2bA eVY6YELGlw2EZoCBzVzlCYawj0SXs8Q= ARC-Seal: i=1; a=rsa-sha256; d=iki.fi; s=meesny; cv=none; t=1785672853; b=gNOU+D6uxLOCe9Nnp7mi8itM4rm/X3n1uxcA5Eh6xV9FW8isRRcne6ww4L/YQhcDcn/thG 07QIif6nCFKPy7ixNs0Eq0k0p+MRrM3+m82IdhUg+mKy6cLE02c/zr72gDmIHWciwlmGSt e4HUEmRrXZTx/GqawGvmNhvf9Wo/XHw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=iki.fi; s=meesny; t=1785672853; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=HpeiDNvEVOrg91qVprA1oDDCAq7S/nXBH9j1ifmqiIc=; b=tGhlxN3ezIhCKepyLjXsB2GvE0UTch4Ac1CCmdGj6bqalhpVli+vCUvgceddjp11m+3e8y O46FkaEKeVntPMABvpAINCPKz8xrSBt30Nd+ObgQiiNiWwkUCrwAkZ9LAjVBndZgl1D6ia 6Nmm/iLiNhiKUARwPQbo9hVkNcVLQEQ= ARC-Authentication-Results: i=1; ORIGINATING; auth=pass smtp.auth=pav smtp.mailfrom=pav@iki.fi From: Pauli Virtanen To: linux-bluetooth@vger.kernel.org Cc: Pauli Virtanen , marcel@holtmann.org, luiz.dentz@gmail.com, oss@fourdim.xyz, linux-kernel@vger.kernel.org, syzbot+e6382a2f53f5fc7453ac@syzkaller.appspotmail.com, syzkaller-bugs@googlegroups.com Subject: [PATCH] Bluetooth: L2CAP: fix race l2cap_sock_cleanup_listen() vs. put_chan Date: Sun, 2 Aug 2026 15:12:28 +0300 Message-ID: X-Mailer: git-send-email 2.55.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" For L2CAP sockets without owning sk->sk_socket, reading l2cap_pi(sk)->chan may race against concurrent l2cap_sock_kill() -> l2cap_sock_put_chan(). This excludes simultaneous proto_ops callbacks, but access in l2cap_sock_cleanup_listen() has unsafe lockless read. Fix the race by taking lock_sock() in l2cap_sock_kill() to synchronize with l2cap_sock_cleanup_listen(). hold_unless_zero() is not needed here, l2cap_pi(sk)->chan owns reference if it is non-NULL. Fixes: 0e2c0392b9dc ("Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_ne= w_connection_cb()") Reported-by: syzbot+e6382a2f53f5fc7453ac@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3De6382a2f53f5fc7453ac Signed-off-by: Pauli Virtanen --- include/net/bluetooth/l2cap.h | 5 +++++ net/bluetooth/l2cap_sock.c | 23 +++++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index ef6ce1c20a4f..3d9a32094347 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -699,7 +699,12 @@ struct l2cap_rx_busy { =20 struct l2cap_pinfo { struct bt_sock bt; + + /* With owning sk_socket chan may be read without lock, other access + * should hold lock_sock. + */ struct l2cap_chan *chan; + struct list_head rx_busy; }; =20 diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 735167f73f31..9540617a0e6c 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1312,7 +1312,12 @@ static void l2cap_sock_kill(struct sock *sk) =20 BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state)); =20 + /* Take lock to synchronize against access without owning sk->sk_socket, + * eg. in l2cap_sock_cleanup_listen(). proto_ops etc. don't need lock. + */ + lock_sock(sk); l2cap_sock_put_chan(sk); + release_sock(sk); =20 /* Kill poor orphan */ sock_set_flag(sk, SOCK_DEAD); @@ -1516,14 +1521,10 @@ static void l2cap_sock_cleanup_listen(struct sock *= parent) * establish sk_lock -> conn->lock and invert the established * conn->lock -> chan->lock -> sk_lock order (lockdep deadlock). * - * Instead, briefly take the child sk lock to fetch and pin its chan. - * l2cap_conn_del() reaches the chan free only via - * l2cap_chan_del() -> l2cap_sock_teardown_cb(), which itself takes - * the child sk lock; holding it across l2cap_chan_hold_unless_zero() - * therefore guarantees the chan cannot be freed while we read and - * pin it (hold_unless_zero() additionally skips a chan already past - * its last reference). We then drop the sk lock before taking - * chan->lock, so sk and chan locks are never held together. + * Instead, briefly take the child sk lock to synchronize vs. + * l2cap_sock_kill that puts l2cap_pi(sk)->chan. We then drop the sk + * lock before taking chan->lock, so sk and chan locks are never held + * together. * * Since we cannot call l2cap_chan_close() without conn->lock, * schedule l2cap_chan_timeout to close the channel; it already @@ -1533,10 +1534,12 @@ static void l2cap_sock_cleanup_listen(struct sock *= parent) struct l2cap_chan *chan; =20 lock_sock_nested(sk, L2CAP_NESTING_NORMAL); - chan =3D l2cap_chan_hold_unless_zero(l2cap_pi(sk)->chan); + chan =3D l2cap_pi(sk)->chan; + if (chan) + l2cap_chan_hold(chan); release_sock(sk); if (!chan) { - /* l2cap_conn_del() already tearing this child down */ + /* Already torn down */ sock_put(sk); continue; } --=20 2.55.0