aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kvm
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 /arch/s390/kvm
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>
Diffstat (limited to 'arch/s390/kvm')
-rw-r--r--arch/s390/kvm/interrupt.c58
1 files changed, 32 insertions, 26 deletions
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: