OSDN Git Service

KVM: s390: Do not leak kernel stack data in the KVM_S390_INTERRUPT ioctl
[android-x86/kernel.git] / arch / s390 / kvm / kvm-s390.c
index 9c7a1ec..ea20b60 100644 (file)
@@ -401,6 +401,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
        case KVM_CAP_S390_RI:
                r = test_facility(64);
                break;
+       case KVM_CAP_S390_BPB:
+               r = test_facility(82);
+               break;
        default:
                r = 0;
        }
@@ -442,6 +445,9 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
        struct kvm_memory_slot *memslot;
        int is_dirty = 0;
 
+       if (kvm_is_ucontrol(kvm))
+               return -EINVAL;
+
        mutex_lock(&kvm->slots_lock);
 
        r = -EINVAL;
@@ -916,7 +922,7 @@ static int kvm_s390_get_machine(struct kvm *kvm, struct kvm_device_attr *attr)
        memcpy(&mach->fac_mask, kvm->arch.model.fac_mask,
               S390_ARCH_FAC_LIST_SIZE_BYTE);
        memcpy((unsigned long *)&mach->fac_list, S390_lowcore.stfle_fac_list,
-              S390_ARCH_FAC_LIST_SIZE_BYTE);
+              sizeof(S390_lowcore.stfle_fac_list));
        if (copy_to_user((void __user *)attr->addr, mach, sizeof(*mach)))
                ret = -EFAULT;
        kfree(mach);
@@ -1437,7 +1443,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 
        /* Populate the facility mask initially. */
        memcpy(kvm->arch.model.fac_mask, S390_lowcore.stfle_fac_list,
-              S390_ARCH_FAC_LIST_SIZE_BYTE);
+              sizeof(S390_lowcore.stfle_fac_list));
        for (i = 0; i < S390_ARCH_FAC_LIST_SIZE_U64; i++) {
                if (i < kvm_s390_fac_list_mask_size())
                        kvm->arch.model.fac_mask[i] &= kvm_s390_fac_list_mask[i];
@@ -1598,6 +1604,7 @@ static void sca_add_vcpu(struct kvm_vcpu *vcpu)
                /* we still need the basic sca for the ipte control */
                vcpu->arch.sie_block->scaoh = (__u32)(((__u64)sca) >> 32);
                vcpu->arch.sie_block->scaol = (__u32)(__u64)sca;
+               return;
        }
        read_lock(&vcpu->kvm->arch.sca_lock);
        if (vcpu->kvm->arch.use_esca) {
@@ -1709,6 +1716,8 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
        kvm_s390_set_prefix(vcpu, 0);
        if (test_kvm_facility(vcpu->kvm, 64))
                vcpu->run->kvm_valid_regs |= KVM_SYNC_RICCB;
+       if (test_kvm_facility(vcpu->kvm, 82))
+               vcpu->run->kvm_valid_regs |= KVM_SYNC_BPBC;
        /* fprs can be synchronized via vrs, even if the guest has no vx. With
         * MACHINE_HAS_VX, (load|store)_fpu_regs() will work with vrs format.
         */
@@ -1825,7 +1834,6 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
        if (test_fp_ctl(current->thread.fpu.fpc))
                /* User space provided an invalid FPC, let's clear it */
                current->thread.fpu.fpc = 0;
-
        save_access_regs(vcpu->arch.host_acrs);
        restore_access_regs(vcpu->run->s.regs.acrs);
        gmap_enable(vcpu->arch.enabled_gmap);
@@ -1873,6 +1881,7 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu)
        current->thread.fpu.fpc = 0;
        vcpu->arch.sie_block->gbea = 1;
        vcpu->arch.sie_block->pp = 0;
+       vcpu->arch.sie_block->fpf &= ~FPF_BPBC;
        vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
        kvm_clear_async_pf_completion_queue(vcpu);
        if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm))
@@ -2740,6 +2749,11 @@ static void sync_regs(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
                if (riccb->valid)
                        vcpu->arch.sie_block->ecb3 |= 0x01;
        }
+       if ((kvm_run->kvm_dirty_regs & KVM_SYNC_BPBC) &&
+           test_kvm_facility(vcpu->kvm, 82)) {
+               vcpu->arch.sie_block->fpf &= ~FPF_BPBC;
+               vcpu->arch.sie_block->fpf |= kvm_run->s.regs.bpbc ? FPF_BPBC : 0;
+       }
 
        kvm_run->kvm_dirty_regs = 0;
 }
@@ -2758,6 +2772,7 @@ static void store_regs(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
        kvm_run->s.regs.pft = vcpu->arch.pfault_token;
        kvm_run->s.regs.pfs = vcpu->arch.pfault_select;
        kvm_run->s.regs.pfc = vcpu->arch.pfault_compare;
+       kvm_run->s.regs.bpbc = (vcpu->arch.sie_block->fpf & FPF_BPBC) == FPF_BPBC;
 }
 
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
@@ -3090,7 +3105,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
        }
        case KVM_S390_INTERRUPT: {
                struct kvm_s390_interrupt s390int;
-               struct kvm_s390_irq s390irq;
+               struct kvm_s390_irq s390irq = {};
 
                r = -EFAULT;
                if (copy_from_user(&s390int, argp, sizeof(s390int)))
@@ -3273,21 +3288,28 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
                                const struct kvm_memory_slot *new,
                                enum kvm_mr_change change)
 {
-       int rc;
-
-       /* If the basics of the memslot do not change, we do not want
-        * to update the gmap. Every update causes several unnecessary
-        * segment translation exceptions. This is usually handled just
-        * fine by the normal fault handler + gmap, but it will also
-        * cause faults on the prefix page of running guest CPUs.
-        */
-       if (old->userspace_addr == mem->userspace_addr &&
-           old->base_gfn * PAGE_SIZE == mem->guest_phys_addr &&
-           old->npages * PAGE_SIZE == mem->memory_size)
-               return;
+       int rc = 0;
 
-       rc = gmap_map_segment(kvm->arch.gmap, mem->userspace_addr,
-               mem->guest_phys_addr, mem->memory_size);
+       switch (change) {
+       case KVM_MR_DELETE:
+               rc = gmap_unmap_segment(kvm->arch.gmap, old->base_gfn * PAGE_SIZE,
+                                       old->npages * PAGE_SIZE);
+               break;
+       case KVM_MR_MOVE:
+               rc = gmap_unmap_segment(kvm->arch.gmap, old->base_gfn * PAGE_SIZE,
+                                       old->npages * PAGE_SIZE);
+               if (rc)
+                       break;
+               /* FALLTHROUGH */
+       case KVM_MR_CREATE:
+               rc = gmap_map_segment(kvm->arch.gmap, mem->userspace_addr,
+                                     mem->guest_phys_addr, mem->memory_size);
+               break;
+       case KVM_MR_FLAGS_ONLY:
+               break;
+       default:
+               WARN(1, "Unknown KVM MR CHANGE: %d\n", change);
+       }
        if (rc)
                pr_warn("failed to commit memory region\n");
        return;