Skip to content

Commit 40c2ffc

Browse files
committed
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
2 parents de353e3 + c61ec3e commit 40c2ffc

8 files changed

Lines changed: 109 additions & 44 deletions

File tree

arch/riscv/kvm/aia.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/irqchip/riscv-imsic.h>
1414
#include <linux/irqdomain.h>
1515
#include <linux/kvm_host.h>
16+
#include <linux/nospec.h>
1617
#include <linux/percpu.h>
1718
#include <linux/spinlock.h>
1819
#include <asm/cpufeature.h>
@@ -182,9 +183,14 @@ int kvm_riscv_vcpu_aia_get_csr(struct kvm_vcpu *vcpu,
182183
unsigned long *out_val)
183184
{
184185
struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
186+
unsigned long regs_max = sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long);
185187

186-
if (reg_num >= sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long))
188+
if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA))
187189
return -ENOENT;
190+
if (reg_num >= regs_max)
191+
return -ENOENT;
192+
193+
reg_num = array_index_nospec(reg_num, regs_max);
188194

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

202-
if (reg_num >= sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long))
209+
if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA))
203210
return -ENOENT;
211+
if (reg_num >= regs_max)
212+
return -ENOENT;
213+
214+
reg_num = array_index_nospec(reg_num, regs_max);
204215

205216
if (kvm_riscv_aia_available()) {
206217
((unsigned long *)csr)[reg_num] = val;

arch/riscv/kvm/aia_aplic.c

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/irqchip/riscv-aplic.h>
1111
#include <linux/kvm_host.h>
1212
#include <linux/math.h>
13+
#include <linux/nospec.h>
1314
#include <linux/spinlock.h>
1415
#include <linux/swab.h>
1516
#include <kvm/iodev.h>
@@ -45,7 +46,7 @@ static u32 aplic_read_sourcecfg(struct aplic *aplic, u32 irq)
4546

4647
if (!irq || aplic->nr_irqs <= irq)
4748
return 0;
48-
irqd = &aplic->irqs[irq];
49+
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
4950

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

6263
if (!irq || aplic->nr_irqs <= irq)
6364
return;
64-
irqd = &aplic->irqs[irq];
65+
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
6566

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

8283
if (!irq || aplic->nr_irqs <= irq)
8384
return 0;
84-
irqd = &aplic->irqs[irq];
85+
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
8586

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

9899
if (!irq || aplic->nr_irqs <= irq)
99100
return;
100-
irqd = &aplic->irqs[irq];
101+
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
101102

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

117118
if (!irq || aplic->nr_irqs <= irq)
118119
return false;
119-
irqd = &aplic->irqs[irq];
120+
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
120121

121122
raw_spin_lock_irqsave(&irqd->lock, flags);
122123
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)
132133

133134
if (!irq || aplic->nr_irqs <= irq)
134135
return;
135-
irqd = &aplic->irqs[irq];
136+
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
136137

137138
raw_spin_lock_irqsave(&irqd->lock, flags);
138139

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

171172
if (!irq || aplic->nr_irqs <= irq)
172173
return false;
173-
irqd = &aplic->irqs[irq];
174+
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
174175

175176
raw_spin_lock_irqsave(&irqd->lock, flags);
176177
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)
186187

187188
if (!irq || aplic->nr_irqs <= irq)
188189
return;
189-
irqd = &aplic->irqs[irq];
190+
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
190191

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

206207
if (!irq || aplic->nr_irqs <= irq)
207208
return false;
208-
irqd = &aplic->irqs[irq];
209+
irqd = &aplic->irqs[array_index_nospec(irq, aplic->nr_irqs)];
209210

210211
raw_spin_lock_irqsave(&irqd->lock, flags);
211212

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

259260
raw_spin_lock_irqsave(&irqd->lock, flags);
260261

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

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

289290
raw_spin_lock_irqsave(&irqd->lock, flags);

arch/riscv/kvm/aia_device.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/irqchip/riscv-imsic.h>
1212
#include <linux/kvm_host.h>
1313
#include <linux/uaccess.h>
14+
#include <linux/cpufeature.h>
1415

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

26+
if (!riscv_isa_extension_available(NULL, SSAIA))
27+
return -ENODEV;
28+
2529
ret = -EBUSY;
2630
if (kvm_trylock_all_vcpus(kvm))
2731
return ret;
@@ -437,7 +441,7 @@ static int aia_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
437441

438442
static int aia_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
439443
{
440-
int nr_vcpus;
444+
int nr_vcpus, r = -ENXIO;
441445

442446
switch (attr->group) {
443447
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)
466470
}
467471
break;
468472
case KVM_DEV_RISCV_AIA_GRP_APLIC:
469-
return kvm_riscv_aia_aplic_has_attr(dev->kvm, attr->attr);
473+
mutex_lock(&dev->kvm->lock);
474+
r = kvm_riscv_aia_aplic_has_attr(dev->kvm, attr->attr);
475+
mutex_unlock(&dev->kvm->lock);
476+
break;
470477
case KVM_DEV_RISCV_AIA_GRP_IMSIC:
471-
return kvm_riscv_aia_imsic_has_attr(dev->kvm, attr->attr);
478+
mutex_lock(&dev->kvm->lock);
479+
r = kvm_riscv_aia_imsic_has_attr(dev->kvm, attr->attr);
480+
mutex_unlock(&dev->kvm->lock);
481+
break;
472482
}
473483

474-
return -ENXIO;
484+
return r;
475485
}
476486

477487
struct kvm_device_ops kvm_riscv_aia_device_ops = {

arch/riscv/kvm/aia_imsic.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,10 @@ int kvm_riscv_vcpu_aia_imsic_rmw(struct kvm_vcpu *vcpu, unsigned long isel,
908908
int r, rc = KVM_INSN_CONTINUE_NEXT_SEPC;
909909
struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
910910

911+
/* If IMSIC vCPU state not initialized then forward to user space */
912+
if (!imsic)
913+
return KVM_INSN_EXIT_TO_USER_SPACE;
914+
911915
if (isel == KVM_RISCV_AIA_IMSIC_TOPEI) {
912916
/* Read pending and enabled interrupt with highest priority */
913917
topei = imsic_mrif_topei(imsic->swfile, imsic->nr_eix,

arch/riscv/kvm/mmu.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
245245
bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
246246
{
247247
struct kvm_gstage gstage;
248+
bool mmu_locked;
248249

249250
if (!kvm->arch.pgd)
250251
return false;
@@ -253,9 +254,12 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
253254
gstage.flags = 0;
254255
gstage.vmid = READ_ONCE(kvm->arch.vmid.vmid);
255256
gstage.pgd = kvm->arch.pgd;
257+
mmu_locked = spin_trylock(&kvm->mmu_lock);
256258
kvm_riscv_gstage_unmap_range(&gstage, range->start << PAGE_SHIFT,
257259
(range->end - range->start) << PAGE_SHIFT,
258260
range->may_block);
261+
if (mmu_locked)
262+
spin_unlock(&kvm->mmu_lock);
259263
return false;
260264
}
261265

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

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

541545
if (writable) {

arch/riscv/kvm/vcpu_fp.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/errno.h>
1111
#include <linux/err.h>
1212
#include <linux/kvm_host.h>
13+
#include <linux/nospec.h>
1314
#include <linux/uaccess.h>
1415
#include <asm/cpufeature.h>
1516

@@ -93,9 +94,11 @@ int kvm_riscv_vcpu_get_reg_fp(struct kvm_vcpu *vcpu,
9394
if (reg_num == KVM_REG_RISCV_FP_F_REG(fcsr))
9495
reg_val = &cntx->fp.f.fcsr;
9596
else if ((KVM_REG_RISCV_FP_F_REG(f[0]) <= reg_num) &&
96-
reg_num <= KVM_REG_RISCV_FP_F_REG(f[31]))
97+
reg_num <= KVM_REG_RISCV_FP_F_REG(f[31])) {
98+
reg_num = array_index_nospec(reg_num,
99+
ARRAY_SIZE(cntx->fp.f.f));
97100
reg_val = &cntx->fp.f.f[reg_num];
98-
else
101+
} else
99102
return -ENOENT;
100103
} else if ((rtype == KVM_REG_RISCV_FP_D) &&
101104
riscv_isa_extension_available(vcpu->arch.isa, d)) {
@@ -107,6 +110,8 @@ int kvm_riscv_vcpu_get_reg_fp(struct kvm_vcpu *vcpu,
107110
reg_num <= KVM_REG_RISCV_FP_D_REG(f[31])) {
108111
if (KVM_REG_SIZE(reg->id) != sizeof(u64))
109112
return -EINVAL;
113+
reg_num = array_index_nospec(reg_num,
114+
ARRAY_SIZE(cntx->fp.d.f));
110115
reg_val = &cntx->fp.d.f[reg_num];
111116
} else
112117
return -ENOENT;
@@ -138,9 +143,11 @@ int kvm_riscv_vcpu_set_reg_fp(struct kvm_vcpu *vcpu,
138143
if (reg_num == KVM_REG_RISCV_FP_F_REG(fcsr))
139144
reg_val = &cntx->fp.f.fcsr;
140145
else if ((KVM_REG_RISCV_FP_F_REG(f[0]) <= reg_num) &&
141-
reg_num <= KVM_REG_RISCV_FP_F_REG(f[31]))
146+
reg_num <= KVM_REG_RISCV_FP_F_REG(f[31])) {
147+
reg_num = array_index_nospec(reg_num,
148+
ARRAY_SIZE(cntx->fp.f.f));
142149
reg_val = &cntx->fp.f.f[reg_num];
143-
else
150+
} else
144151
return -ENOENT;
145152
} else if ((rtype == KVM_REG_RISCV_FP_D) &&
146153
riscv_isa_extension_available(vcpu->arch.isa, d)) {
@@ -152,6 +159,8 @@ int kvm_riscv_vcpu_set_reg_fp(struct kvm_vcpu *vcpu,
152159
reg_num <= KVM_REG_RISCV_FP_D_REG(f[31])) {
153160
if (KVM_REG_SIZE(reg->id) != sizeof(u64))
154161
return -EINVAL;
162+
reg_num = array_index_nospec(reg_num,
163+
ARRAY_SIZE(cntx->fp.d.f));
155164
reg_val = &cntx->fp.d.f[reg_num];
156165
} else
157166
return -ENOENT;

0 commit comments

Comments
 (0)