Bluetooth: SCO: Fix UAF on sco_sock_timeout

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: syzbot+4c0d0c4cde787116d465@syzkaller.appspotmail.com
Tested-by: syzbot+4c0d0c4cde787116d465@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=4c0d0c4cde787116d465
Fixes: ba316be1b6 ("Bluetooth: schedule SCO timeouts with delayed_work")
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Luiz Augusto von Dentz 2024-10-22 12:31:08 -04:00
parent 989fa5171f
commit 1bf4470a39
3 changed files with 35 additions and 6 deletions

View File

@ -403,6 +403,7 @@ int bt_sock_register(int proto, const struct net_proto_family *ops);
void bt_sock_unregister(int proto); void bt_sock_unregister(int proto);
void bt_sock_link(struct bt_sock_list *l, struct sock *s); void bt_sock_link(struct bt_sock_list *l, struct sock *s);
void bt_sock_unlink(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);
struct sock *bt_sock_alloc(struct net *net, struct socket *sock, struct sock *bt_sock_alloc(struct net *net, struct socket *sock,
struct proto *prot, int proto, gfp_t prio, int kern); struct proto *prot, int proto, gfp_t prio, int kern);
int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,

View File

@ -185,6 +185,28 @@ void bt_sock_unlink(struct bt_sock_list *l, struct sock *sk)
} }
EXPORT_SYMBOL(bt_sock_unlink); 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) void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh)
{ {
const struct cred *old_cred; const struct cred *old_cred;

View File

@ -76,6 +76,16 @@ struct sco_pinfo {
#define SCO_CONN_TIMEOUT (HZ * 40) #define SCO_CONN_TIMEOUT (HZ * 40)
#define SCO_DISCONN_TIMEOUT (HZ * 2) #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) static void sco_sock_timeout(struct work_struct *work)
{ {
struct sco_conn *conn = container_of(work, struct sco_conn, struct sco_conn *conn = container_of(work, struct sco_conn,
@ -87,9 +97,7 @@ static void sco_sock_timeout(struct work_struct *work)
sco_conn_unlock(conn); sco_conn_unlock(conn);
return; return;
} }
sk = conn->sk; sk = sco_sock_hold(conn);
if (sk)
sock_hold(sk);
sco_conn_unlock(conn); sco_conn_unlock(conn);
if (!sk) if (!sk)
@ -194,9 +202,7 @@ static void sco_conn_del(struct hci_conn *hcon, int err)
/* Kill socket */ /* Kill socket */
sco_conn_lock(conn); sco_conn_lock(conn);
sk = conn->sk; sk = sco_sock_hold(conn);
if (sk)
sock_hold(sk);
sco_conn_unlock(conn); sco_conn_unlock(conn);
if (sk) { if (sk) {