mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 1/2] xen: privcmd: Switch from mutex to spinlock for irqfds
@ 2024-06-18  9:42 Viresh Kumar
  2024-06-18  9:42 ` [PATCH 2/2] xen: privcmd: Fix possible access to a freed kirqfd instance Viresh Kumar
  2024-07-02 10:12 ` [PATCH 1/2] xen: privcmd: Switch from mutex to spinlock for irqfds Juergen Gross
  0 siblings, 2 replies; 5+ messages in thread
From: Viresh Kumar @ 2024-06-18  9:42 UTC (permalink / raw)
  To: Juergen Gross, Stefano Stabellini, Oleksandr Tyshchenko
  Cc: Viresh Kumar, Vincent Guittot, Alex Bennée,
	Manos Pitsidianakis, Paolo Bonzini, Al Viro, xen-devel,
	linux-kernel

irqfd_wakeup() gets EPOLLHUP, when it is called by
eventfd_release() by way of wake_up_poll(&ctx->wqh, EPOLLHUP), which
gets called under spin_lock_irqsave(). We can't use a mutex here as it
will lead to a deadlock.

Fix it by switching over to a spin lock.

Reported-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 drivers/xen/privcmd.c | 26 +++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c
index 67dfa4778864..5ceb6c56cf3e 100644
--- a/drivers/xen/privcmd.c
+++ b/drivers/xen/privcmd.c
@@ -13,7 +13,6 @@
 #include <linux/file.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/mutex.h>
 #include <linux/poll.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
@@ -845,7 +844,7 @@ static long privcmd_ioctl_mmap_resource(struct file *file,
 #ifdef CONFIG_XEN_PRIVCMD_EVENTFD
 /* Irqfd support */
 static struct workqueue_struct *irqfd_cleanup_wq;
-static DEFINE_MUTEX(irqfds_lock);
+static DEFINE_SPINLOCK(irqfds_lock);
 static LIST_HEAD(irqfds_list);
 
 struct privcmd_kernel_irqfd {
@@ -909,9 +908,11 @@ irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode, int sync, void *key)
 		irqfd_inject(kirqfd);
 
 	if (flags & EPOLLHUP) {
-		mutex_lock(&irqfds_lock);
+		unsigned long flags;
+
+		spin_lock_irqsave(&irqfds_lock, flags);
 		irqfd_deactivate(kirqfd);
-		mutex_unlock(&irqfds_lock);
+		spin_unlock_irqrestore(&irqfds_lock, flags);
 	}
 
 	return 0;
@@ -929,6 +930,7 @@ irqfd_poll_func(struct file *file, wait_queue_head_t *wqh, poll_table *pt)
 static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
 {
 	struct privcmd_kernel_irqfd *kirqfd, *tmp;
+	unsigned long flags;
 	__poll_t events;
 	struct fd f;
 	void *dm_op;
@@ -968,18 +970,18 @@ static int privcmd_irqfd_assign(struct privcmd_irqfd *irqfd)
 	init_waitqueue_func_entry(&kirqfd->wait, irqfd_wakeup);
 	init_poll_funcptr(&kirqfd->pt, irqfd_poll_func);
 
-	mutex_lock(&irqfds_lock);
+	spin_lock_irqsave(&irqfds_lock, flags);
 
 	list_for_each_entry(tmp, &irqfds_list, list) {
 		if (kirqfd->eventfd == tmp->eventfd) {
 			ret = -EBUSY;
-			mutex_unlock(&irqfds_lock);
+			spin_unlock_irqrestore(&irqfds_lock, flags);
 			goto error_eventfd;
 		}
 	}
 
 	list_add_tail(&kirqfd->list, &irqfds_list);
-	mutex_unlock(&irqfds_lock);
+	spin_unlock_irqrestore(&irqfds_lock, flags);
 
 	/*
 	 * Check if there was an event already pending on the eventfd before we
@@ -1011,12 +1013,13 @@ static int privcmd_irqfd_deassign(struct privcmd_irqfd *irqfd)
 {
 	struct privcmd_kernel_irqfd *kirqfd;
 	struct eventfd_ctx *eventfd;
+	unsigned long flags;
 
 	eventfd = eventfd_ctx_fdget(irqfd->fd);
 	if (IS_ERR(eventfd))
 		return PTR_ERR(eventfd);
 
-	mutex_lock(&irqfds_lock);
+	spin_lock_irqsave(&irqfds_lock, flags);
 
 	list_for_each_entry(kirqfd, &irqfds_list, list) {
 		if (kirqfd->eventfd == eventfd) {
@@ -1025,7 +1028,7 @@ static int privcmd_irqfd_deassign(struct privcmd_irqfd *irqfd)
 		}
 	}
 
-	mutex_unlock(&irqfds_lock);
+	spin_unlock_irqrestore(&irqfds_lock, flags);
 
 	eventfd_ctx_put(eventfd);
 
@@ -1073,13 +1076,14 @@ static int privcmd_irqfd_init(void)
 static void privcmd_irqfd_exit(void)
 {
 	struct privcmd_kernel_irqfd *kirqfd, *tmp;
+	unsigned long flags;
 
-	mutex_lock(&irqfds_lock);
+	spin_lock_irqsave(&irqfds_lock, flags);
 
 	list_for_each_entry_safe(kirqfd, tmp, &irqfds_list, list)
 		irqfd_deactivate(kirqfd);
 
-	mutex_unlock(&irqfds_lock);
+	spin_unlock_irqrestore(&irqfds_lock, flags);
 
 	destroy_workqueue(irqfd_cleanup_wq);
 }
-- 
2.31.1.272.g89b43f80a514


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2/2] xen: privcmd: Fix possible access to a freed kirqfd instance
  2024-06-18  9:42 [PATCH 1/2] xen: privcmd: Switch from mutex to spinlock for irqfds Viresh Kumar
@ 2024-06-18  9:42 ` Viresh Kumar
  2024-07-02 10:15   ` Juergen Gross
  2024-07-02 10:12 ` [PATCH 1/2] xen: privcmd: Switch from mutex to spinlock for irqfds Juergen Gross
  1 sibling, 1 reply; 5+ messages in thread
From: Viresh Kumar @ 2024-06-18  9:42 UTC (permalink / raw)
  To: Juergen Gross, Stefano Stabellini, Oleksandr Tyshchenko
  Cc: Viresh Kumar, Vincent Guittot, Alex Bennée,
	Manos Pitsidianakis, Paolo Bonzini, Al Viro, xen-devel,
	linux-kernel

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 <viro@zeniv.linux.org.uk>
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 drivers/xen/privcmd.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c
index 5ceb6c56cf3e..041774750e52 100644
--- a/drivers/xen/privcmd.c
+++ b/drivers/xen/privcmd.c
@@ -16,6 +16,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>
@@ -845,6 +846,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 {
@@ -872,6 +874,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);
@@ -934,7 +939,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)
@@ -980,6 +985,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);
 
@@ -991,6 +997,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.
-- 
2.31.1.272.g89b43f80a514


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] xen: privcmd: Switch from mutex to spinlock for irqfds
  2024-06-18  9:42 [PATCH 1/2] xen: privcmd: Switch from mutex to spinlock for irqfds Viresh Kumar
  2024-06-18  9:42 ` [PATCH 2/2] xen: privcmd: Fix possible access to a freed kirqfd instance Viresh Kumar
@ 2024-07-02 10:12 ` Juergen Gross
  2024-07-02 10:17   ` Viresh Kumar
  1 sibling, 1 reply; 5+ messages in thread
From: Juergen Gross @ 2024-07-02 10:12 UTC (permalink / raw)
  To: Viresh Kumar, Stefano Stabellini, Oleksandr Tyshchenko
  Cc: Vincent Guittot, Alex Bennée, Manos Pitsidianakis,
	Paolo Bonzini, Al Viro, xen-devel, linux-kernel


[-- Attachment #1.1.1: Type: text/plain, Size: 1044 bytes --]

On 18.06.24 11:42, Viresh Kumar wrote:
> irqfd_wakeup() gets EPOLLHUP, when it is called by
> eventfd_release() by way of wake_up_poll(&ctx->wqh, EPOLLHUP), which
> gets called under spin_lock_irqsave(). We can't use a mutex here as it
> will lead to a deadlock.
> 
> Fix it by switching over to a spin lock.
> 
> Reported-by: Al Viro <viro@zeniv.linux.org.uk>
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> ---
>   drivers/xen/privcmd.c | 26 +++++++++++++++-----------
>   1 file changed, 15 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c
> index 67dfa4778864..5ceb6c56cf3e 100644
> --- a/drivers/xen/privcmd.c
> +++ b/drivers/xen/privcmd.c
> @@ -13,7 +13,6 @@
>   #include <linux/file.h>
>   #include <linux/kernel.h>
>   #include <linux/module.h>
> -#include <linux/mutex.h>

I don't think you can drop that. There is still the ioreq_lock mutex.

I can fix that up while committing, with that:

Reviewed-by: Juergen Gross <jgross@suse.com>


Juergen

[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] xen: privcmd: Fix possible access to a freed kirqfd instance
  2024-06-18  9:42 ` [PATCH 2/2] xen: privcmd: Fix possible access to a freed kirqfd instance Viresh Kumar
@ 2024-07-02 10:15   ` Juergen Gross
  0 siblings, 0 replies; 5+ messages in thread
From: Juergen Gross @ 2024-07-02 10:15 UTC (permalink / raw)
  To: Viresh Kumar, Stefano Stabellini, Oleksandr Tyshchenko
  Cc: Vincent Guittot, Alex Bennée, Manos Pitsidianakis,
	Paolo Bonzini, Al Viro, xen-devel, linux-kernel


[-- Attachment #1.1.1: Type: text/plain, Size: 835 bytes --]

On 18.06.24 11:42, Viresh Kumar wrote:
> 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 <viro@zeniv.linux.org.uk>
> Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>

Reviewed-by: Juergen Gross <jgross@suse.com>


Juergen


[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] xen: privcmd: Switch from mutex to spinlock for irqfds
  2024-07-02 10:12 ` [PATCH 1/2] xen: privcmd: Switch from mutex to spinlock for irqfds Juergen Gross
@ 2024-07-02 10:17   ` Viresh Kumar
  0 siblings, 0 replies; 5+ messages in thread
From: Viresh Kumar @ 2024-07-02 10:17 UTC (permalink / raw)
  To: Juergen Gross
  Cc: Stefano Stabellini, Oleksandr Tyshchenko, Vincent Guittot,
	Alex Bennée, Manos Pitsidianakis, Paolo Bonzini, Al Viro,
	xen-devel, linux-kernel

On 02-07-24, 12:12, Juergen Gross wrote:
> On 18.06.24 11:42, Viresh Kumar wrote:
> > irqfd_wakeup() gets EPOLLHUP, when it is called by
> > eventfd_release() by way of wake_up_poll(&ctx->wqh, EPOLLHUP), which
> > gets called under spin_lock_irqsave(). We can't use a mutex here as it
> > will lead to a deadlock.
> > 
> > Fix it by switching over to a spin lock.
> > 
> > Reported-by: Al Viro <viro@zeniv.linux.org.uk>
> > Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> > ---
> >   drivers/xen/privcmd.c | 26 +++++++++++++++-----------
> >   1 file changed, 15 insertions(+), 11 deletions(-)
> > 
> > diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c
> > index 67dfa4778864..5ceb6c56cf3e 100644
> > --- a/drivers/xen/privcmd.c
> > +++ b/drivers/xen/privcmd.c
> > @@ -13,7 +13,6 @@
> >   #include <linux/file.h>
> >   #include <linux/kernel.h>
> >   #include <linux/module.h>
> > -#include <linux/mutex.h>
> 
> I don't think you can drop that. There is still the ioreq_lock mutex.

You are right. The header got included from somewhere else I believe
since the build didn't fail for me.

> I can fix that up while committing, with that:
> 
> Reviewed-by: Juergen Gross <jgross@suse.com>

Thanks.

-- 
viresh

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2024-07-02 10:17 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-18  9:42 [PATCH 1/2] xen: privcmd: Switch from mutex to spinlock for irqfds Viresh Kumar
2024-06-18  9:42 ` [PATCH 2/2] xen: privcmd: Fix possible access to a freed kirqfd instance Viresh Kumar
2024-07-02 10:15   ` Juergen Gross
2024-07-02 10:12 ` [PATCH 1/2] xen: privcmd: Switch from mutex to spinlock for irqfds Juergen Gross
2024-07-02 10:17   ` Viresh Kumar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®