Commit 40c2ffca authored by Paolo Bonzini's avatar Paolo Bonzini
Browse files

Merge tag 'kvm-riscv-fixes-7.0-1' of https://github.com/kvm-riscv/linux into HEAD

KVM/riscv fixes for 7.0, take #1

- Prevent speculative out-of-bounds access using array_index_nospec()
  in APLIC interrupt handling, ONE_REG regiser access, AIA CSR access,
  float register access, and PMU counter access
- Fix potential use-after-free issues in kvm_riscv_gstage_get_leaf(),
  kvm_riscv_aia_aplic_has_attr(), and kvm_riscv_aia_imsic_has_attr()
- Fix potential null pointer dereference in kvm_riscv_vcpu_aia_rmw_topei()
- Fix off-by-one array access in SBI PMU
- Skip THP support check during dirty logging
- Fix error code returned for Smstateen and Ssaia ONE_REG interface
- Check host Ssaia extension when creating AIA irqchip
parents de353e3f c61ec3e8
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/irqchip/riscv-imsic.h>
#include <linux/irqdomain.h>
#include <linux/kvm_host.h>
#include <linux/nospec.h>
#include <linux/percpu.h>
#include <linux/spinlock.h>
#include <asm/cpufeature.h>
@@ -182,9 +183,14 @@ int kvm_riscv_vcpu_aia_get_csr(struct kvm_vcpu *vcpu,
			       unsigned long *out_val)
{
	struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
	unsigned long regs_max = sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long);

	if (reg_num >= sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long))
	if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA))
		return -ENOENT;
	if (reg_num >= regs_max)
		return -ENOENT;

	reg_num = array_index_nospec(reg_num, regs_max);

	*out_val = 0;
	if (kvm_riscv_aia_available())
@@ -198,9 +204,14 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu,
			       unsigned long val)
{
	struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
	unsigned long regs_max = sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long);

	if (reg_num >= sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long))
	if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA))
		return -ENOENT;
	if (reg_num >= regs_max)
		return -ENOENT;

	reg_num = array_index_nospec(reg_num, regs_max);

	if (kvm_riscv_aia_available()) {
		((unsigned long *)csr)[reg_num] = val;
+12 −11
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/irqchip/riscv-aplic.h>
#include <linux/kvm_host.h>
#include <linux/math.h>
#include <linux/nospec.h>
#include <linux/spinlock.h>
#include <linux/swab.h>
#include <kvm/iodev.h>
@@ -45,7 +46,7 @@ static u32 aplic_read_sourcecfg(struct aplic *aplic, u32 irq)

	if (!irq || aplic->nr_irqs <= irq)
		return 0;
	irqd = &aplic->irqs[irq];
	irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];

	raw_spin_lock_irqsave(&irqd->lock, flags);
	ret = irqd->sourcecfg;
@@ -61,7 +62,7 @@ static void aplic_write_sourcecfg(struct aplic *aplic, u32 irq, u32 val)

	if (!irq || aplic->nr_irqs <= irq)
		return;
	irqd = &aplic->irqs[irq];
	irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];

	if (val & APLIC_SOURCECFG_D)
		val = 0;
@@ -81,7 +82,7 @@ static u32 aplic_read_target(struct aplic *aplic, u32 irq)

	if (!irq || aplic->nr_irqs <= irq)
		return 0;
	irqd = &aplic->irqs[irq];
	irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];

	raw_spin_lock_irqsave(&irqd->lock, flags);
	ret = irqd->target;
@@ -97,7 +98,7 @@ static void aplic_write_target(struct aplic *aplic, u32 irq, u32 val)

	if (!irq || aplic->nr_irqs <= irq)
		return;
	irqd = &aplic->irqs[irq];
	irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];

	val &= APLIC_TARGET_EIID_MASK |
	       (APLIC_TARGET_HART_IDX_MASK << APLIC_TARGET_HART_IDX_SHIFT) |
@@ -116,7 +117,7 @@ static bool aplic_read_pending(struct aplic *aplic, u32 irq)

	if (!irq || aplic->nr_irqs <= irq)
		return false;
	irqd = &aplic->irqs[irq];
	irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];

	raw_spin_lock_irqsave(&irqd->lock, flags);
	ret = (irqd->state & APLIC_IRQ_STATE_PENDING) ? true : false;
@@ -132,7 +133,7 @@ static void aplic_write_pending(struct aplic *aplic, u32 irq, bool pending)

	if (!irq || aplic->nr_irqs <= irq)
		return;
	irqd = &aplic->irqs[irq];
	irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];

	raw_spin_lock_irqsave(&irqd->lock, flags);

@@ -170,7 +171,7 @@ static bool aplic_read_enabled(struct aplic *aplic, u32 irq)

	if (!irq || aplic->nr_irqs <= irq)
		return false;
	irqd = &aplic->irqs[irq];
	irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];

	raw_spin_lock_irqsave(&irqd->lock, flags);
	ret = (irqd->state & APLIC_IRQ_STATE_ENABLED) ? true : false;
@@ -186,7 +187,7 @@ static void aplic_write_enabled(struct aplic *aplic, u32 irq, bool enabled)

	if (!irq || aplic->nr_irqs <= irq)
		return;
	irqd = &aplic->irqs[irq];
	irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];

	raw_spin_lock_irqsave(&irqd->lock, flags);
	if (enabled)
@@ -205,7 +206,7 @@ static bool aplic_read_input(struct aplic *aplic, u32 irq)

	if (!irq || aplic->nr_irqs <= irq)
		return false;
	irqd = &aplic->irqs[irq];
	irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];

	raw_spin_lock_irqsave(&irqd->lock, flags);

@@ -254,7 +255,7 @@ static void aplic_update_irq_range(struct kvm *kvm, u32 first, u32 last)
	for (irq = first; irq <= last; irq++) {
		if (!irq || aplic->nr_irqs <= irq)
			continue;
		irqd = &aplic->irqs[irq];
		irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];

		raw_spin_lock_irqsave(&irqd->lock, flags);

@@ -283,7 +284,7 @@ int kvm_riscv_aia_aplic_inject(struct kvm *kvm, u32 source, bool level)

	if (!aplic || !source || (aplic->nr_irqs <= source))
		return -ENODEV;
	irqd = &aplic->irqs[source];
	irqd = &aplic->irqs[array_index_nospec(source, aplic->nr_irqs)];
	ie = (aplic->domaincfg & APLIC_DOMAINCFG_IE) ? true : false;

	raw_spin_lock_irqsave(&irqd->lock, flags);
+14 −4
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include <linux/irqchip/riscv-imsic.h>
#include <linux/kvm_host.h>
#include <linux/uaccess.h>
#include <linux/cpufeature.h>

static int aia_create(struct kvm_device *dev, u32 type)
{
@@ -22,6 +23,9 @@ static int aia_create(struct kvm_device *dev, u32 type)
	if (irqchip_in_kernel(kvm))
		return -EEXIST;

	if (!riscv_isa_extension_available(NULL, SSAIA))
		return -ENODEV;

	ret = -EBUSY;
	if (kvm_trylock_all_vcpus(kvm))
		return ret;
@@ -437,7 +441,7 @@ static int aia_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr)

static int aia_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
{
	int nr_vcpus;
	int nr_vcpus, r = -ENXIO;

	switch (attr->group) {
	case KVM_DEV_RISCV_AIA_GRP_CONFIG:
@@ -466,12 +470,18 @@ static int aia_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
		}
		break;
	case KVM_DEV_RISCV_AIA_GRP_APLIC:
		return kvm_riscv_aia_aplic_has_attr(dev->kvm, attr->attr);
		mutex_lock(&dev->kvm->lock);
		r = kvm_riscv_aia_aplic_has_attr(dev->kvm, attr->attr);
		mutex_unlock(&dev->kvm->lock);
		break;
	case KVM_DEV_RISCV_AIA_GRP_IMSIC:
		return kvm_riscv_aia_imsic_has_attr(dev->kvm, attr->attr);
		mutex_lock(&dev->kvm->lock);
		r = kvm_riscv_aia_imsic_has_attr(dev->kvm, attr->attr);
		mutex_unlock(&dev->kvm->lock);
		break;
	}

	return -ENXIO;
	return r;
}

struct kvm_device_ops kvm_riscv_aia_device_ops = {
+4 −0
Original line number Diff line number Diff line
@@ -908,6 +908,10 @@ int kvm_riscv_vcpu_aia_imsic_rmw(struct kvm_vcpu *vcpu, unsigned long isel,
	int r, rc = KVM_INSN_CONTINUE_NEXT_SEPC;
	struct imsic *imsic = vcpu->arch.aia_context.imsic_state;

	/* If IMSIC vCPU state not initialized then forward to user space */
	if (!imsic)
		return KVM_INSN_EXIT_TO_USER_SPACE;

	if (isel == KVM_RISCV_AIA_IMSIC_TOPEI) {
		/* Read pending and enabled interrupt with highest priority */
		topei = imsic_mrif_topei(imsic->swfile, imsic->nr_eix,
+5 −1
Original line number Diff line number Diff line
@@ -245,6 +245,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
{
	struct kvm_gstage gstage;
	bool mmu_locked;

	if (!kvm->arch.pgd)
		return false;
@@ -253,9 +254,12 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
	gstage.flags = 0;
	gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid);
	gstage.pgd = kvm->arch.pgd;
	mmu_locked = spin_trylock(&kvm->mmu_lock);
	kvm_riscv_gstage_unmap_range(&gstage, range->start << PAGE_SHIFT,
				     (range->end - range->start) << PAGE_SHIFT,
				     range->may_block);
	if (mmu_locked)
		spin_unlock(&kvm->mmu_lock);
	return false;
}

@@ -535,7 +539,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
		goto out_unlock;

	/* Check if we are backed by a THP and thus use block mapping if possible */
	if (vma_pagesize == PAGE_SIZE)
	if (!logging && (vma_pagesize == PAGE_SIZE))
		vma_pagesize = transparent_hugepage_adjust(kvm, memslot, hva, &hfn, &gpa);

	if (writable) {
Loading