aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Freimann <jfrei@linux.vnet.ibm.com>2015-03-16 07:17:13 -0400
committerChristian Borntraeger <borntraeger@de.ibm.com>2015-03-31 15:05:51 -0400
commit94aa033efcac47b09db22cb561e135baf37b7887 (patch)
tree2370d7dbd93fd307db16399de6229dbb0aa46d04
parenta3ed8dae6e3db479ca275883ba7fe994170b0ae6 (diff)
KVM: s390: fix get_all_floating_irqs
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. Cc: <stable@vger.kernel.org> # 3.18+: 69a8d4562638 KVM: s390: no need to hold... Reviewed-by: David Hildenbrand <dahi@linux.vnet.ibm.com> Signed-off-by: Jens Freimann <jfrei@linux.vnet.ibm.com> Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com> Acked-by: Cornelia Huck <cornelia.huck@de.ibm.com>
-rw-r--r--Documentation/virtual/kvm/devices/s390_flic.txt3
-rw-r--r--arch/s390/kvm/interrupt.c58
2 files changed, 35 insertions, 26 deletions
diff --git a/Documentation/virtual/kvm/devices/s390_flic.txt b/Documentation/virtual/kvm/devices/s390_flic.txt
index 4ceef53164b0..d1ad9d5cae46 100644
--- a/Documentation/virtual/kvm/devices/s390_flic.txt
+++ b/Documentation/virtual/kvm/devices/s390_flic.txt
@@ -27,6 +27,9 @@ Groups:
27 Copies all floating interrupts into a buffer provided by userspace. 27 Copies all floating interrupts into a buffer provided by userspace.
28 When the buffer is too small it returns -ENOMEM, which is the indication 28 When the buffer is too small it returns -ENOMEM, which is the indication
29 for userspace to try again with a bigger buffer. 29 for userspace to try again with a bigger buffer.
30 -ENOBUFS is returned when the allocation of a kernelspace buffer has
31 failed.
32 -EFAULT is returned when copying data to userspace failed.
30 All interrupts remain pending, i.e. are not deleted from the list of 33 All interrupts remain pending, i.e. are not deleted from the list of
31 currently pending interrupts. 34 currently pending interrupts.
32 attr->addr contains the userspace address of the buffer into which all 35 attr->addr contains the userspace address of the buffer into which all
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 2361b8ed0a50..5ebd500e6400 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -17,6 +17,7 @@
17#include <linux/signal.h> 17#include <linux/signal.h>
18#include <linux/slab.h> 18#include <linux/slab.h>
19#include <linux/bitmap.h> 19#include <linux/bitmap.h>
20#include <linux/vmalloc.h>
20#include <asm/asm-offsets.h> 21#include <asm/asm-offsets.h>
21#include <asm/dis.h> 22#include <asm/dis.h>
22#include <asm/uaccess.h> 23#include <asm/uaccess.h>
@@ -1477,61 +1478,66 @@ void kvm_s390_clear_float_irqs(struct kvm *kvm)
1477 spin_unlock(&fi->lock); 1478 spin_unlock(&fi->lock);
1478} 1479}
1479 1480
1480static inline int copy_irq_to_user(struct kvm_s390_interrupt_info *inti, 1481static void inti_to_irq(struct kvm_s390_interrupt_info *inti,
1481 u8 *addr) 1482 struct kvm_s390_irq *irq)
1482{ 1483{
1483 struct kvm_s390_irq __user *uptr = (struct kvm_s390_irq __user *) addr; 1484 irq->type = inti->type;
1484 struct kvm_s390_irq irq = {0};
1485
1486 irq.type = inti->type;
1487 switch (inti->type) { 1485 switch (inti->type) {
1488 case KVM_S390_INT_PFAULT_INIT: 1486 case KVM_S390_INT_PFAULT_INIT:
1489 case KVM_S390_INT_PFAULT_DONE: 1487 case KVM_S390_INT_PFAULT_DONE:
1490 case KVM_S390_INT_VIRTIO: 1488 case KVM_S390_INT_VIRTIO:
1491 case KVM_S390_INT_SERVICE: 1489 case KVM_S390_INT_SERVICE:
1492 irq.u.ext = inti->ext; 1490 irq->u.ext = inti->ext;
1493 break; 1491 break;
1494 case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: 1492 case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
1495 irq.u.io = inti->io; 1493 irq->u.io = inti->io;
1496 break; 1494 break;
1497 case KVM_S390_MCHK: 1495 case KVM_S390_MCHK:
1498 irq.u.mchk = inti->mchk; 1496 irq->u.mchk = inti->mchk;
1499 break; 1497 break;
1500 default:
1501 return -EINVAL;
1502 } 1498 }
1503
1504 if (copy_to_user(uptr, &irq, sizeof(irq)))
1505 return -EFAULT;
1506
1507 return 0;
1508} 1499}
1509 1500
1510static int get_all_floating_irqs(struct kvm *kvm, __u8 *buf, __u64 len) 1501static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len)
1511{ 1502{
1512 struct kvm_s390_interrupt_info *inti; 1503 struct kvm_s390_interrupt_info *inti;
1513 struct kvm_s390_float_interrupt *fi; 1504 struct kvm_s390_float_interrupt *fi;
1505 struct kvm_s390_irq *buf;
1506 int max_irqs;
1514 int ret = 0; 1507 int ret = 0;
1515 int n = 0; 1508 int n = 0;
1516 1509
1510 if (len > KVM_S390_FLIC_MAX_BUFFER || len == 0)
1511 return -EINVAL;
1512
1513 /*
1514 * We are already using -ENOMEM to signal
1515 * userspace it may retry with a bigger buffer,
1516 * so we need to use something else for this case
1517 */
1518 buf = vzalloc(len);
1519 if (!buf)
1520 return -ENOBUFS;
1521
1522 max_irqs = len / sizeof(struct kvm_s390_irq);
1523
1517 fi = &kvm->arch.float_int; 1524 fi = &kvm->arch.float_int;
1518 spin_lock(&fi->lock); 1525 spin_lock(&fi->lock);
1519
1520 list_for_each_entry(inti, &fi->list, list) { 1526 list_for_each_entry(inti, &fi->list, list) {
1521 if (len < sizeof(struct kvm_s390_irq)) { 1527 if (n == max_irqs) {
1522 /* signal userspace to try again */ 1528 /* signal userspace to try again */
1523 ret = -ENOMEM; 1529 ret = -ENOMEM;
1524 break; 1530 break;
1525 } 1531 }
1526 ret = copy_irq_to_user(inti, buf); 1532 inti_to_irq(inti, &buf[n]);
1527 if (ret)
1528 break;
1529 buf += sizeof(struct kvm_s390_irq);
1530 len -= sizeof(struct kvm_s390_irq);
1531 n++; 1533 n++;
1532 } 1534 }
1533
1534 spin_unlock(&fi->lock); 1535 spin_unlock(&fi->lock);
1536 if (!ret && n > 0) {
1537 if (copy_to_user(usrbuf, buf, sizeof(struct kvm_s390_irq) * n))
1538 ret = -EFAULT;
1539 }
1540 vfree(buf);
1535 1541
1536 return ret < 0 ? ret : n; 1542 return ret < 0 ? ret : n;
1537} 1543}
@@ -1542,7 +1548,7 @@ static int flic_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
1542 1548
1543 switch (attr->group) { 1549 switch (attr->group) {
1544 case KVM_DEV_FLIC_GET_ALL_IRQS: 1550 case KVM_DEV_FLIC_GET_ALL_IRQS:
1545 r = get_all_floating_irqs(dev->kvm, (u8 *) attr->addr, 1551 r = get_all_floating_irqs(dev->kvm, (u8 __user *) attr->addr,
1546 attr->attr); 1552 attr->attr);
1547 break; 1553 break;
1548 default: 1554 default: