Commit ca673429 authored by Luiz Augusto von Dentz's avatar Luiz Augusto von Dentz Committed by Dong Chenchen
Browse files

Bluetooth: SCO: Fix UAF on sco_sock_timeout

mainline inclusion
from mainline-v6.12-rc5
commit 1bf4470a3939c678fb822073e9ea77a0560bc6bb
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IB2BXB
CVE: CVE-2024-50125

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1bf4470a3939c678fb822073e9ea77a0560bc6bb



--------------------------------

conn->sk maybe have been unlinked/freed while waiting for sco_conn_lock
so this checks if the conn->sk is still valid by checking if it part of
sco_sk_list.

Reported-by: default avatar <syzbot+4c0d0c4cde787116d465@syzkaller.appspotmail.com>
Tested-by: default avatar <syzbot+4c0d0c4cde787116d465@syzkaller.appspotmail.com>
Closes: https://syzkaller.appspot.com/bug?extid=4c0d0c4cde787116d465


Fixes: ba316be1 ("Bluetooth: schedule SCO timeouts with delayed_work")
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Conflicts:
	net/bluetooth/af_bluetooth.c
	include/net/bluetooth/bluetooth.h
[commit 464c702f and 6bfa273e wasnt merged, which lead to
context conflicts.]
Signed-off-by: default avatarDong Chenchen <dongchenchen2@huawei.com>
parent c70d1dd9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -267,6 +267,7 @@ int bt_sock_register(int proto, const struct net_proto_family *ops);
void bt_sock_unregister(int proto);
void bt_sock_link(struct bt_sock_list *l, struct sock *s);
void bt_sock_unlink(struct bt_sock_list *l, struct sock *s);
bool bt_sock_linked(struct bt_sock_list *l, struct sock *s);
int  bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
		     int flags);
int  bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg,
+22 −0
Original line number Diff line number Diff line
@@ -154,6 +154,28 @@ void bt_sock_unlink(struct bt_sock_list *l, struct sock *sk)
}
EXPORT_SYMBOL(bt_sock_unlink);

bool bt_sock_linked(struct bt_sock_list *l, struct sock *s)
{
	struct sock *sk;

	if (!l || !s)
		return false;

	read_lock(&l->lock);

	sk_for_each(sk, &l->head) {
		if (s == sk) {
			read_unlock(&l->lock);
			return true;
		}
	}

	read_unlock(&l->lock);

	return false;
}
EXPORT_SYMBOL(bt_sock_linked);

void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh)
{
	BT_DBG("parent %p, sk %p", parent, sk);
+12 −6
Original line number Diff line number Diff line
@@ -75,6 +75,16 @@ struct sco_pinfo {
#define SCO_CONN_TIMEOUT	(HZ * 40)
#define SCO_DISCONN_TIMEOUT	(HZ * 2)

static struct sock *sco_sock_hold(struct sco_conn *conn)
{
	if (!conn || !bt_sock_linked(&sco_sk_list, conn->sk))
		return NULL;

	sock_hold(conn->sk);

	return conn->sk;
}

static void sco_sock_timeout(struct work_struct *work)
{
	struct sco_conn *conn = container_of(work, struct sco_conn,
@@ -86,9 +96,7 @@ static void sco_sock_timeout(struct work_struct *work)
		sco_conn_unlock(conn);
		return;
	}
	sk = conn->sk;
	if (sk)
		sock_hold(sk);
	sk = sco_sock_hold(conn);
	sco_conn_unlock(conn);

	if (!sk)
@@ -191,9 +199,7 @@ static void sco_conn_del(struct hci_conn *hcon, int err)

	/* Kill socket */
	sco_conn_lock(conn);
	sk = conn->sk;
	if (sk)
		sock_hold(sk);
	sk = sco_sock_hold(conn);
	sco_conn_unlock(conn);

	if (sk) {