From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-oo1-f70.google.com (mail-oo1-f70.google.com [209.85.161.70]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 32C3E22A4E1 for ; Sun, 9 Aug 2026 12:23:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.70 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786278202; cv=none; b=HDPHYlMebL8NEoIFRO2gRdF1hY8mCB14lui5VVjcgTWI/a39APHRGTy2U+7TohF+zxvKCfpvp2JEobDGNhkAR4LDpBj8vO8mVSoWGUNBPtZ4njGiyOIGIHrtOTmbYS6/wtoMtuflXGYlwC0KUlIBgkbxnJe/pRz7urIwPVruRLY= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786278202; c=relaxed/simple; bh=d4cMOSOkXbNYYZ4Vic6la949YkWl8FOAkbjyA+rzH2Y=; h=MIME-Version:Date:In-Reply-To:Message-ID:Subject:From:To:Cc: Content-Type; b=KQfxtqp5ejkMGEPt2AZJkUwDszfzN4WabStELOysCIC+qlnnIaMEIrP+L7WxeV3uU8rMHqoO08Giu02qS8J596nF7jvaoruoxkRrKL1cxzUxdDA15amTiIeyLRPpsI8dBV9+7yldxNGB28P/Lo+c4J+VEDLqC23TzSar+jJ+dkE= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=syzkaller.appspotmail.com; spf=pass smtp.mailfrom=M3KW2WVRGUFZ5GODRSRYTGD7.apphosting.bounces.google.com; arc=none smtp.client-ip=209.85.161.70 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=syzkaller.appspotmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=M3KW2WVRGUFZ5GODRSRYTGD7.apphosting.bounces.google.com Received: by mail-oo1-f70.google.com with SMTP id 006d021491bc7-6aae6f87ed7so1103515eaf.0 for ; Sun, 09 Aug 2026 05:23:20 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786278200; x=1786883000; h=content-type:cc:to:from:subject:message-id:in-reply-to:date :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to:content-type; bh=2fFpWuqBCFf2YbpUgmp/WdKT6wkaaISq/EYbPsTvhII=; b=VweFD9QKW9TBSNWSa7gIotfGtv9mqAipftGmAF2c40oynbjWjUtk+KE6PWi/h8K8Su jPDm++PUMt8Ht/4XLAV3D9IH4JO7z/Sr6La9Cth/hLESj23YlCWYUuMc7LGBNitZ+QUA BPyvzBk8EED8jGKXvN+XnYMAN5KPRpCBbDC9z34+LRIENL8ZH52OwsX1u0OJjILmyCyt Rne+c3wdc/JdLf+NP2AZ1e0TjuYshCtQus8E5mXMzLLIIhidLkepDJKaHWSsKjB4Mj0Z 3MXNib3rRbjNLssO4uDPmYvXUjdhiV4NNVHGE52xOHQbXMX6zI/9ZWmYY6FBRH9LFBD/ PoFQ== X-Forwarded-Encrypted: i=1; AHgh+RpC68IMmYjr/URILcMqRiiYV15zi1sCPBso17HWVL1aj/bBxDvH1hRjTfEtDu2uf4iJ40LqpxHWIKgifuU=@vger.kernel.org X-Gm-Message-State: AOJu0YxI3LBkWKbIvVC2a2PUSHJVr3hKO1pOB5Y+VmdTZDyCNMX/fOIF tecrjkz/ACWF7lPtMsCqI3sj0SxmZCSedcTp+k6ikiaGb/YMQ76Sr6iCoTKrh6RZQCKa8p4JpS4 BJtJHOCVcD6x8URFDM7O1Eejk7jxa+8vlUIU2YcWopR//laGzXhzWmo602Ek= Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Received: by 2002:a05:6820:1ca8:b0:6ac:b6c0:2c09 with SMTP id 006d021491bc7-6b0211af6a3mr11640211eaf.1.1786278200161; Sun, 09 Aug 2026 05:23:20 -0700 (PDT) Date: Sun, 09 Aug 2026 05:23:20 -0700 In-Reply-To: X-Google-Appengine-App-Id: s~syzkaller X-Google-Appengine-App-Id-Alias: syzkaller Message-ID: <6a787138.9c11d2ce.289b96.00cf.GAE@google.com> Subject: Re: [PATCH] Bluetooth: L2CAP: access chan->conn safely in get/setsockopt From: syzbot To: pav@iki.fi Cc: pav@iki.fi, syzkaller-bugs@googlegroups.com, linux-kernel@vger.kernel.org Content-Type: text/plain; charset="UTF-8" > 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. This can be guaranteed by > synchronizing with l2cap_sock_teardown_cb(). Make l2cap_chan::conn > readable without l2cap_chan::lock, so we can access with only sk lock. > Also move sock_set_flag() inside lock_sock() since it is not atomic. > > Fixes: b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") > Reported-by: syzbot+b106284c2a0b7bc80cf9@syzkaller.appspotmail.com > --- > > #syz test This crash does not have a reproducer. I cannot test it. > > net/bluetooth/l2cap_core.c | 2 +- > net/bluetooth/l2cap_sock.c | 64 ++++++++++++++++++++++++++++---------- > 2 files changed, 48 insertions(+), 18 deletions(-) > > diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c > index ee459dd411f5..7327ebb1e710 100644 > --- a/net/bluetooth/l2cap_core.c > +++ b/net/bluetooth/l2cap_core.c > @@ -624,7 +624,7 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) > > conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; > > - chan->conn = l2cap_conn_get(conn); > + WRITE_ONCE(chan->conn, l2cap_conn_get(conn)); > > switch (chan->chan_type) { > case L2CAP_CHAN_CONN_ORIENTED: > diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c > index 735167f73f31..c3d9f7721963 100644 > --- a/net/bluetooth/l2cap_sock.c > +++ b/net/bluetooth/l2cap_sock.c > @@ -436,11 +436,33 @@ static int l2cap_get_mode(struct l2cap_chan *chan) > return -EINVAL; > } > > +static struct l2cap_conn *l2cap_sock_conn(struct sock *sk) > +{ > + struct l2cap_chan *chan = l2cap_pi(sk)->chan; > + struct l2cap_conn *conn = READ_ONCE(chan->conn); > + > + lockdep_assert(lockdep_sock_is_held(sk)); > + > + /* chan holds refcount on conn during its lifetime, so if non-NULL > + * observed, it is valid. > + * > + * conn holds refcount on conn->hcon, but the hci_conn may be in deleted > + * state. l2cap_sock_teardown_cb() is called before associated hci_conn > + * is deleted, so it is alive if sk is not zapped, as long as sk lock is > + * held. > + */ > + if (sock_flag(sk, SOCK_ZAPPED)) > + return NULL; > + > + return conn; > +} > + > static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, > sockopt_t *sopt) > { > 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; > @@ -537,9 +559,15 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, > break; > } > > + conn = l2cap_sock_conn(sk); > + 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_iter(&cinfo, len, &sopt->iter_out) != len) > @@ -561,6 +589,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; > int len, mode, err = 0; > @@ -589,12 +618,14 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, > break; > } > > + conn = l2cap_sock_conn(sk); > + > 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; > } > @@ -678,12 +709,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_sock_conn(sk); > + > + if (sk->sk_state != BT_CONNECTED || !conn) { > err = -ENOTCONN; > break; > } > > - opt = hci_conn_get_phy(chan->conn->hcon); > + opt = hci_conn_get_phy(conn->hcon); > > if (copy_to_iter(&opt, sizeof(opt), &sopt->iter_out) != > sizeof(opt)) > @@ -938,11 +971,10 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, > > chan->sec_level = sec.level; > > - if (!chan->conn) > + conn = l2cap_sock_conn(sk); > + 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)) { > @@ -997,7 +1029,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, > } > > if (opt == BT_FLUSHABLE_OFF) { > - conn = chan->conn; > + conn = l2cap_sock_conn(sk); > + > /* proceed further only when we have l2cap_conn and > No Flush support in the LM */ > if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) { > @@ -1083,7 +1116,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, > break; > > case BT_PHY: > - if (sk->sk_state != BT_CONNECTED) { > + conn = l2cap_sock_conn(sk); > + if (sk->sk_state != BT_CONNECTED || !conn) { > err = -ENOTCONN; > break; > } > @@ -1093,10 +1127,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, > if (err) > break; > > - if (!chan->conn) > - break; > - > - conn = chan->conn; > err = hci_conn_set_phy(conn->hcon, phys); > break; > > @@ -1716,11 +1746,11 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err) > > break; > } > - release_sock(sk); > > /* Only zap after cleanup to avoid use after free race */ > sock_set_flag(sk, SOCK_ZAPPED); > > + release_sock(sk); > } > > static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state, > -- > 2.55.0 >