Skip to content

Commit

Permalink
xen: privcmd: Fix possible access to a freed kirqfd instance
Browse files Browse the repository at this point in the history
Nothing prevents simultaneous ioctl calls to privcmd_irqfd_assign() and
privcmd_irqfd_deassign(). If that happens, it is possible that a kirqfd
created and added to the irqfds_list by privcmd_irqfd_assign() may get
removed by another thread executing privcmd_irqfd_deassign(), while the
former is still using it after dropping the locks.

This can lead to a situation where an already freed kirqfd instance may
be accessed and cause kernel oops.

Use SRCU locking to prevent the same, as is done for the KVM
implementation for irqfds.

Reported-by: Al Viro <[email protected]>
Suggested-by: Paolo Bonzini <[email protected]>
Signed-off-by: Viresh Kumar <[email protected]>
Reviewed-by: Juergen Gross <[email protected]>
Link: https://lore.kernel.org/r/9e884af1f1f842eacbb7afc5672c8feb4dea7f3f.1718703669.git.viresh.kumar@linaro.org
Signed-off-by: Juergen Gross <[email protected]>
  • Loading branch information
vireshk authored and jgross1 committed Jul 2, 2024
1 parent 1c68259 commit 611ff1b
Showing 1 changed file with 9 additions and 1 deletion.
10 changes: 9 additions & 1 deletion drivers/xen/privcmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/srcu.h>
#include <linux/string.h>
#include <linux/workqueue.h>
#include <linux/errno.h>
Expand Down Expand Up @@ -847,6 +848,7 @@ static long privcmd_ioctl_mmap_resource(struct file *file,
/* Irqfd support */
static struct workqueue_struct *irqfd_cleanup_wq;
static DEFINE_SPINLOCK(irqfds_lock);
DEFINE_STATIC_SRCU(irqfds_srcu);
static LIST_HEAD(irqfds_list);

struct privcmd_kernel_irqfd {
Expand Down Expand Up @@ -874,6 +876,9 @@ static void irqfd_shutdown(struct work_struct *work)
container_of(work, struct privcmd_kernel_irqfd, shutdown);
u64 cnt;

/* Make sure irqfd has been initialized in assign path */
synchronize_srcu(&irqfds_srcu);

eventfd_ctx_remove_wait_queue(kirqfd->eventfd, &kirqfd->wait, &cnt);
eventfd_ctx_put(kirqfd->eventfd);
kfree(kirqfd);
Expand Down Expand Up @@ -936,7 +941,7 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
__poll_t events;
struct fd f;
void *dm_op;
int ret;
int ret, idx;

kirqfd = kzalloc(sizeof(*kirqfd) + irqfd->size, GFP_KERNEL);
if (!kirqfd)
Expand Down Expand Up @@ -982,6 +987,7 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
}
}

idx = srcu_read_lock(&irqfds_srcu);
list_add_tail(&kirqfd->list, &irqfds_list);
spin_unlock_irqrestore(&irqfds_lock, flags);

Expand All @@ -993,6 +999,8 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
if (events & EPOLLIN)
irqfd_inject(kirqfd);

srcu_read_unlock(&irqfds_srcu, idx);

/*
* Do not drop the file until the kirqfd is fully initialized, otherwise
* we might race against the EPOLLHUP.
Expand Down

0 comments on commit 611ff1b

Please sign in to comment.