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:
parent
989fa5171f
commit
1bf4470a39
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user