From: Pauli Virtanen <pav@iki.fi> stable inclusion from stable-v6.18.44 commit 3b921533e8aa95b77aadcf31737595578e735f3c category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/18470 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit fdfde532ab1caa165fcd8985001157ac8b4db365 ] iso_conn_del() and iso_chan_del() have a race that results to double-put of iso_conn: [Task hdev->workqueue] [Task 2] iso_conn_del iso_chan_del iso_conn_hold_unless_zero iso_conn_lock iso_conn_lock conn->sk = NULL iso_conn_unlock sk = iso_sock_hold(conn) <---------´ if (!sk) iso_conn_put iso_conn_put iso_conn_put /* UAF */ The extra put for !sk in iso_conn_del() is currently required since failing iso_chan_add() may leave iso_conn not associated with any sk. Fix by having iso_pi(sk)->conn own refcount when non-NULL, so iso_conn_del does not need to put it. Adjust the iso_conn_add() refcounting so that conn is put if it does not get associated with an sk. Fixes: dc26097bdb86 ("Bluetooth: ISO: Use kref to track lifetime of iso_conn") Signed-off-by: Pauli Virtanen <pav@iki.fi> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Signed-off-by: Sasha Levin <sashal@kernel.org> Conflicts: net/bluetooth/iso.c [Context conflicts] Signed-off-by: Yao Yiqi <yaoyiqi3@huawei.com> --- net/bluetooth/iso.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index cbe670247ddd..5bcf7d2e1625 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -105,9 +105,6 @@ static void iso_conn_free(struct kref *ref) BT_DBG("conn %p", conn); - if (conn->sk) - iso_pi(conn->sk)->conn = NULL; - if (conn->hcon) { conn->hcon->iso_data = NULL; if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags)) @@ -142,6 +139,14 @@ static struct iso_conn *iso_conn_hold_unless_zero(struct iso_conn *conn) return conn; } +static struct iso_conn *iso_conn_hold(struct iso_conn *conn) +{ + BT_DBG("conn %p refcnt %u", conn, kref_read(&conn->ref)); + + kref_get(&conn->ref); + return conn; +} + static struct sock *iso_sock_hold(struct iso_conn *conn) { if (!conn || !bt_sock_linked(&iso_sk_list, conn->sk)) @@ -207,7 +212,6 @@ static struct iso_conn *iso_conn_add(struct hci_conn *hcon) conn->hcon = hcon; iso_conn_unlock(conn); } - iso_conn_put(conn); return conn; } @@ -287,10 +291,8 @@ static void iso_conn_del(struct hci_conn *hcon, int err) sk = iso_sock_hold(conn); iso_conn_unlock(conn); - if (!sk) { - iso_conn_put(conn); + if (!sk) goto done; - } iso_sock_disable_timer(sk); @@ -346,7 +348,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk, return -EIO; } - iso_pi(sk)->conn = conn; + iso_pi(sk)->conn = iso_conn_hold(conn); conn->sk = sk; clear_bit(ISO_CONN_DROPPED, conn->flags); @@ -447,6 +449,7 @@ static int iso_connect_bis(struct sock *sk) lock_sock(sk); err = iso_chan_add(conn, sk, NULL); + iso_conn_put(conn); if (err) { release_sock(sk); goto unlock; @@ -544,6 +547,7 @@ static int iso_connect_cis(struct sock *sk) lock_sock(sk); err = iso_chan_add(conn, sk, NULL); + iso_conn_put(conn); if (err) { release_sock(sk); goto unlock; @@ -2110,8 +2114,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status) struct iso_conn *conn; conn = iso_conn_add(hcon); - if (conn) + if (conn) { iso_conn_ready(conn); + iso_conn_put(conn); + } } else { iso_conn_del(hcon, bt_to_errno(status)); } -- 2.34.1