From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793095372; b=YXD6zrVZ236qZL13NWRgwRrnlFSe81p0PH3MIdrr+3ns94aSFTuwIKvL5NBk2+HIOmTo ttOhZj6sPLKwXKYtPVP0yk2Gos1Z30sA6GdIgqNzUKSFQjUdNcBTGRMjqo/7EQQs6hQeD dEEOSTexAKB+at8D8DRv6zrk3QHBY6xdWjYXu4Ia5BjOxZXCrInKmo54/Af3B8InodBm+ XrQPf4Aqyc7aHXHd3wBXeiELF+O9AQOe961wZd5tC3rxwEEYQcakOl1oA61XNTNkyRD3g 5kV3+oEkJ0kjI3MvXhp8UucqUliFlGuJijqN6QQeOjOOQXYxuTP7ELJQkEGE9qnR3xQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793095372; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:Content-Transfer-Encoding; bh=5pQdM8kOJ6UaQOiYmKNyNzgDJ8CI4a2oMImStPvrk9M=; b=ejHjZAg/iBKEIJUqVq/VFmzFMFfKpfoCEz8OsuYaDng5W/9cDMyzmlkSsCOm3bQkQ/pZ Vwv+Jbl9TZYQp5wKzaokf1N9C2ClKi3oUqLp928WXCCdA/VeEGZjL6F9U0k9z8kOqLz2y CDR2EGjGMJ71YjMId1fGl3PTjN9csWheUBwKVP+Jgib0qwHpVLQKAiiYBmIVwXMzyzHdh 1OviOBpFD2lEbsmtlBqnkBDopkO0bA1/ckAa5REkq+1qdW9CiBVp3Slgs36bMwFAcciCP BHHjsIBy70Cw5DWlj/aydYv9TVHdQTm/RwE5EkPHGR2MOM5FYlMTLRq7x/eC134AtPQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b="Bu4xAJu/"; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 44AB5606A324; Tue, 27 Oct 2026 10:02:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=Bu4xAJu/ Received: by smtp.kernel.org (Postfix) with ESMTPSA id 333A11F031E0; Tue, 27 Oct 2026 10:02:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1793095372; bh=5pQdM8kOJ6UaQOiYmKNyNzgDJ8CI4a2oMImStPvrk9M=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Bu4xAJu/1r+hr90M9SPctxxbTjmSuFjdSHkcaPPf9U+LQe48wu/l8JYmXnn92AtYo W7GuC0bMK4Z1sNItrU/2O+DpLowTlAqmDRGcpVHihMyScu03I+gbPKJ8Sq+iAMAQa0 snE0zzqIU6pU6/gi6/PknndJ285VSXgBIwJVUqC1VvMEVoX2o0ab2ufzKlDzIW7orv 2IGYnwcoxLquv/+I3oQISt2OAybnkVWf72qLQXPpdejvdxq2vntewO5xIR2kJesP05 eVSCV3HPyXb+onQUYUmpxgQ4V136r0Z2nREV74yLUUPMR9ytgO7jfzG6phS6H/OxaQ 6+xRWSDb6fuEQ== From: Lee Jones To: lee@kernel.org, Marcel Holtmann , Johan Hedberg , Luiz Augusto von Dentz , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , "Gustavo F. Padovan" , Maxim Krasnyansky , Marco Elver , linux-bluetooth@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org Cc: stable@vger.kernel.org, Pauli Virtanen , syzbot+b106284c2a0b7bc80cf9@syzkaller.appspotmail.com, Luiz Augusto von Dentz Subject: [STABLE v6.1-v5.10 1/2] Bluetooth: L2CAP: access chan->conn safely in get/setsockopt Date: Tue, 27 Oct 2026 10:02:39 +0000 Message-ID: <20261027100242.2886699-2-lee@kernel.org> X-Mailer: git-send-email 2.56.0.307.g5fe7bd680c-goog In-Reply-To: <20261027100242.2886699-1-lee@kernel.org> References: <20261027100242.2886699-1-lee@kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Pauli Virtanen [ Upstream commit ca2c4c26498643f421d35ffe258fafbd3ed461c3 ] Since commit b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") l2cap_chan::conn has held reference and remains non-NULL also after the corresponding hci_conn is deleted. In this state accessing various fields eg. hci_conn::hdev is invalid, which leads to KASAN crash in l2cap_sock_setsockopt() access of conn->hcon->hdev. Check l2cap_chan::conn.hcon corresponds to an alive hci_conn before trying to use it in l2cap_sock.c. Hold l2cap_chan_lock() in getsockopt/setsockopt to ensure it stays alive, and to avoid data races in l2cap_chan fields. Fixes: b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") Reported-by: syzbot+b106284c2a0b7bc80cf9@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=b106284c2a0b7bc80cf9 Signed-off-by: Pauli Virtanen Signed-off-by: Luiz Augusto von Dentz [Lee: Omit sockopt_t conversion from commit bf01627cc04b ("Bluetooth: L2CAP: convert to getsockopt_iter")] Signed-off-by: Lee Jones --- net/bluetooth/l2cap_sock.c | 57 ++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 9b8bddefeb84..b11558b0dfec 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -437,11 +437,26 @@ static int l2cap_get_mode(struct l2cap_chan *chan) return -EINVAL; } +static struct l2cap_conn *l2cap_chan_conn(struct l2cap_chan *chan) +{ + lockdep_assert_held(&chan->lock); + + /* l2cap_conn_del() sets FLAG_DEL while holding chan->lock before + * conn->hcon is deleted. If not set and conn is non-NULL, conn->hcon + * remains alive during this chan->lock critical section. + */ + if (test_bit(FLAG_DEL, &chan->flags)) + return NULL; + + return chan->conn; +} + static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; + struct l2cap_conn *conn; struct l2cap_options opts; struct l2cap_conninfo cinfo; int err = 0; @@ -453,6 +468,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, if (get_user(len, optlen)) return -EFAULT; + l2cap_chan_lock(chan); lock_sock(sk); switch (optname) { @@ -538,9 +554,15 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, break; } + conn = l2cap_chan_conn(chan); + if (!conn) { + err = -ENOTCONN; + break; + } + memset(&cinfo, 0, sizeof(cinfo)); - cinfo.hci_handle = chan->conn->hcon->handle; - memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3); + cinfo.hci_handle = conn->hcon->handle; + memcpy(cinfo.dev_class, conn->hcon->dev_class, 3); len = min(len, sizeof(cinfo)); if (copy_to_user(optval, (char *) &cinfo, len)) @@ -554,6 +576,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, } release_sock(sk); + l2cap_chan_unlock(chan); + return err; } @@ -562,6 +586,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; + struct l2cap_conn *conn; struct bt_security sec; struct bt_power pwr; u32 phys; @@ -578,6 +603,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, if (get_user(len, optlen)) return -EFAULT; + l2cap_chan_lock(chan); lock_sock(sk); switch (optname) { @@ -589,12 +615,14 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, break; } + conn = l2cap_chan_conn(chan); + memset(&sec, 0, sizeof(sec)); - if (chan->conn) { - sec.level = chan->conn->hcon->sec_level; + if (conn) { + sec.level = conn->hcon->sec_level; if (sk->sk_state == BT_CONNECTED) - sec.key_size = chan->conn->hcon->enc_key_size; + sec.key_size = conn->hcon->enc_key_size; } else { sec.level = chan->sec_level; } @@ -670,12 +698,14 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, break; case BT_PHY: - if (sk->sk_state != BT_CONNECTED) { + conn = l2cap_chan_conn(chan); + + if (sk->sk_state != BT_CONNECTED || !conn) { err = -ENOTCONN; break; } - phys = hci_conn_get_phy(chan->conn->hcon); + phys = hci_conn_get_phy(conn->hcon); if (put_user(phys, (u32 __user *) optval)) err = -EFAULT; @@ -708,6 +738,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, } release_sock(sk); + l2cap_chan_unlock(chan); return err; } @@ -738,6 +769,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, BT_DBG("sk %p", sk); + l2cap_chan_lock(chan); lock_sock(sk); switch (optname) { @@ -838,6 +870,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, } release_sock(sk); + l2cap_chan_unlock(chan); return err; } @@ -901,6 +934,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, if (level != SOL_BLUETOOTH) return -ENOPROTOOPT; + l2cap_chan_lock(chan); lock_sock(sk); switch (optname) { @@ -926,11 +960,10 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, chan->sec_level = sec.level; - if (!chan->conn) + conn = l2cap_chan_conn(chan); + if (!conn) break; - conn = chan->conn; - /* change security for LE channels */ if (chan->scid == L2CAP_CID_ATT) { if (smp_conn_security(conn->hcon, sec.level)) { @@ -985,7 +1018,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, } if (opt == BT_FLUSHABLE_OFF) { - conn = chan->conn; + conn = l2cap_chan_conn(chan); + /* proceed further only when we have l2cap_conn and No Flush support in the LM */ if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) { @@ -1108,6 +1142,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, } release_sock(sk); + l2cap_chan_unlock(chan); return err; } -- 2.56.0.307.g5fe7bd680c-goog