lkml.org 
[lkml]   [2015]   [May]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 3.19 025/177] KVM: s390: fix get_all_floating_irqs
    Date
    3.19-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Jens Freimann <jfrei@linux.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@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>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.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:



    \
     
     \ /
      Last update: 2015-05-02 22:01    [W:4.022 / U:0.028 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site