[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20150502190120.780806366@linuxfoundation.org>
Date: Sat, 2 May 2015 21:00:47 +0200
From: Greg Kroah-Hartman <gregkh@...uxfoundation.org>
To: linux-kernel@...r.kernel.org
Cc: Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
stable@...r.kernel.org,
David Hildenbrand <dahi@...ux.vnet.ibm.com>,
Jens Freimann <jfrei@...ux.vnet.ibm.com>,
Christian Borntraeger <borntraeger@...ibm.com>,
Cornelia Huck <cornelia.huck@...ibm.com>
Subject: [PATCH 3.19 025/177] KVM: s390: fix get_all_floating_irqs
3.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jens Freimann <jfrei@...ux.vnet.ibm.com>
commit 94aa033efcac47b09db22cb561e135baf37b7887 upstream.
This fixes a bug introduced with commit c05c4186bbe4 ("KVM: s390:
add floating irq controller").
get_all_floating_irqs() does copy_to_user() while holding
a spin lock. Let's fix this by filling a temporary buffer
first and copy it to userspace after giving up the lock.
Reviewed-by: David Hildenbrand <dahi@...ux.vnet.ibm.com>
Signed-off-by: Jens Freimann <jfrei@...ux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@...ibm.com>
Acked-by: Cornelia Huck <cornelia.huck@...ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@...uxfoundation.org>
---
Documentation/virtual/kvm/devices/s390_flic.txt | 3 +
arch/s390/kvm/interrupt.c | 58 +++++++++++++-----------
2 files changed, 35 insertions(+), 26 deletions(-)
--- a/Documentation/virtual/kvm/devices/s390_flic.txt
+++ b/Documentation/virtual/kvm/devices/s390_flic.txt
@@ -27,6 +27,9 @@ Groups:
Copies all floating interrupts into a buffer provided by userspace.
When the buffer is too small it returns -ENOMEM, which is the indication
for userspace to try again with a bigger buffer.
+ -ENOBUFS is returned when the allocation of a kernelspace buffer has
+ failed.
+ -EFAULT is returned when copying data to userspace failed.
All interrupts remain pending, i.e. are not deleted from the list of
currently pending interrupts.
attr->addr contains the userspace address of the buffer into which all
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -17,6 +17,7 @@
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/bitmap.h>
+#include <linux/vmalloc.h>
#include <asm/asm-offsets.h>
#include <asm/uaccess.h>
#include "kvm-s390.h"
@@ -1386,61 +1387,66 @@ void kvm_s390_clear_float_irqs(struct kv
spin_unlock(&fi->lock);
}
-static inline int copy_irq_to_user(struct kvm_s390_interrupt_info *inti,
- u8 *addr)
+static void inti_to_irq(struct kvm_s390_interrupt_info *inti,
+ struct kvm_s390_irq *irq)
{
- struct kvm_s390_irq __user *uptr = (struct kvm_s390_irq __user *) addr;
- struct kvm_s390_irq irq = {0};
-
- irq.type = inti->type;
+ irq->type = inti->type;
switch (inti->type) {
case KVM_S390_INT_PFAULT_INIT:
case KVM_S390_INT_PFAULT_DONE:
case KVM_S390_INT_VIRTIO:
case KVM_S390_INT_SERVICE:
- irq.u.ext = inti->ext;
+ irq->u.ext = inti->ext;
break;
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
- irq.u.io = inti->io;
+ irq->u.io = inti->io;
break;
case KVM_S390_MCHK:
- irq.u.mchk = inti->mchk;
+ irq->u.mchk = inti->mchk;
break;
- default:
- return -EINVAL;
}
-
- if (copy_to_user(uptr, &irq, sizeof(irq)))
- return -EFAULT;
-
- return 0;
}
-static int get_all_floating_irqs(struct kvm *kvm, __u8 *buf, __u64 len)
+static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len)
{
struct kvm_s390_interrupt_info *inti;
struct kvm_s390_float_interrupt *fi;
+ struct kvm_s390_irq *buf;
+ int max_irqs;
int ret = 0;
int n = 0;
+ if (len > KVM_S390_FLIC_MAX_BUFFER || len == 0)
+ return -EINVAL;
+
+ /*
+ * We are already using -ENOMEM to signal
+ * userspace it may retry with a bigger buffer,
+ * so we need to use something else for this case
+ */
+ buf = vzalloc(len);
+ if (!buf)
+ return -ENOBUFS;
+
+ max_irqs = len / sizeof(struct kvm_s390_irq);
+
fi = &kvm->arch.float_int;
spin_lock(&fi->lock);
-
list_for_each_entry(inti, &fi->list, list) {
- if (len < sizeof(struct kvm_s390_irq)) {
+ if (n == max_irqs) {
/* signal userspace to try again */
ret = -ENOMEM;
break;
}
- ret = copy_irq_to_user(inti, buf);
- if (ret)
- break;
- buf += sizeof(struct kvm_s390_irq);
- len -= sizeof(struct kvm_s390_irq);
+ inti_to_irq(inti, &buf[n]);
n++;
}
-
spin_unlock(&fi->lock);
+ if (!ret && n > 0) {
+ if (copy_to_user(usrbuf, buf, sizeof(struct kvm_s390_irq) * n))
+ ret = -EFAULT;
+ }
+ vfree(buf);
return ret < 0 ? ret : n;
}
@@ -1451,7 +1457,7 @@ static int flic_get_attr(struct kvm_devi
switch (attr->group) {
case KVM_DEV_FLIC_GET_ALL_IRQS:
- r = get_all_floating_irqs(dev->kvm, (u8 *) attr->addr,
+ r = get_all_floating_irqs(dev->kvm, (u8 __user *) attr->addr,
attr->attr);
break;
default:
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists