Skip to content

Commit f9e26fc

Browse files
Lukas Gerlachavpatel
authored andcommitted
KVM: riscv: Fix Spectre-v1 in ONE_REG register access
User-controlled register indices from the ONE_REG ioctl are used to index into arrays of register values. Sanitize them with array_index_nospec() to prevent speculative out-of-bounds access. Reviewed-by: Radim Krčmář <radim.krcmar@oss.qualcomm.com> Signed-off-by: Lukas Gerlach <lukas.gerlach@cispa.de> Link: https://lore.kernel.org/r/20260303-kvm-riscv-spectre-v1-v2-1-192caab8e0dc@cispa.de Signed-off-by: Anup Patel <anup@brainfault.org>
1 parent b342166 commit f9e26fc

1 file changed

Lines changed: 28 additions & 8 deletions

File tree

arch/riscv/kvm/vcpu_onereg.c

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/bitops.h>
1111
#include <linux/errno.h>
1212
#include <linux/err.h>
13+
#include <linux/nospec.h>
1314
#include <linux/uaccess.h>
1415
#include <linux/kvm_host.h>
1516
#include <asm/cacheflush.h>
@@ -127,6 +128,7 @@ static int kvm_riscv_vcpu_isa_check_host(unsigned long kvm_ext, unsigned long *g
127128
kvm_ext >= ARRAY_SIZE(kvm_isa_ext_arr))
128129
return -ENOENT;
129130

131+
kvm_ext = array_index_nospec(kvm_ext, ARRAY_SIZE(kvm_isa_ext_arr));
130132
*guest_ext = kvm_isa_ext_arr[kvm_ext];
131133
switch (*guest_ext) {
132134
case RISCV_ISA_EXT_SMNPM:
@@ -443,13 +445,16 @@ static int kvm_riscv_vcpu_get_reg_core(struct kvm_vcpu *vcpu,
443445
unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
444446
KVM_REG_SIZE_MASK |
445447
KVM_REG_RISCV_CORE);
448+
unsigned long regs_max = sizeof(struct kvm_riscv_core) / sizeof(unsigned long);
446449
unsigned long reg_val;
447450

448451
if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long))
449452
return -EINVAL;
450-
if (reg_num >= sizeof(struct kvm_riscv_core) / sizeof(unsigned long))
453+
if (reg_num >= regs_max)
451454
return -ENOENT;
452455

456+
reg_num = array_index_nospec(reg_num, regs_max);
457+
453458
if (reg_num == KVM_REG_RISCV_CORE_REG(regs.pc))
454459
reg_val = cntx->sepc;
455460
else if (KVM_REG_RISCV_CORE_REG(regs.pc) < reg_num &&
@@ -476,13 +481,16 @@ static int kvm_riscv_vcpu_set_reg_core(struct kvm_vcpu *vcpu,
476481
unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
477482
KVM_REG_SIZE_MASK |
478483
KVM_REG_RISCV_CORE);
484+
unsigned long regs_max = sizeof(struct kvm_riscv_core) / sizeof(unsigned long);
479485
unsigned long reg_val;
480486

481487
if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long))
482488
return -EINVAL;
483-
if (reg_num >= sizeof(struct kvm_riscv_core) / sizeof(unsigned long))
489+
if (reg_num >= regs_max)
484490
return -ENOENT;
485491

492+
reg_num = array_index_nospec(reg_num, regs_max);
493+
486494
if (copy_from_user(&reg_val, uaddr, KVM_REG_SIZE(reg->id)))
487495
return -EFAULT;
488496

@@ -507,10 +515,13 @@ static int kvm_riscv_vcpu_general_get_csr(struct kvm_vcpu *vcpu,
507515
unsigned long *out_val)
508516
{
509517
struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
518+
unsigned long regs_max = sizeof(struct kvm_riscv_csr) / sizeof(unsigned long);
510519

511-
if (reg_num >= sizeof(struct kvm_riscv_csr) / sizeof(unsigned long))
520+
if (reg_num >= regs_max)
512521
return -ENOENT;
513522

523+
reg_num = array_index_nospec(reg_num, regs_max);
524+
514525
if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) {
515526
kvm_riscv_vcpu_flush_interrupts(vcpu);
516527
*out_val = (csr->hvip >> VSIP_TO_HVIP_SHIFT) & VSIP_VALID_MASK;
@@ -526,10 +537,13 @@ static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu,
526537
unsigned long reg_val)
527538
{
528539
struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
540+
unsigned long regs_max = sizeof(struct kvm_riscv_csr) / sizeof(unsigned long);
529541

530-
if (reg_num >= sizeof(struct kvm_riscv_csr) / sizeof(unsigned long))
542+
if (reg_num >= regs_max)
531543
return -ENOENT;
532544

545+
reg_num = array_index_nospec(reg_num, regs_max);
546+
533547
if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) {
534548
reg_val &= VSIP_VALID_MASK;
535549
reg_val <<= VSIP_TO_HVIP_SHIFT;
@@ -548,11 +562,14 @@ static inline int kvm_riscv_vcpu_smstateen_set_csr(struct kvm_vcpu *vcpu,
548562
unsigned long reg_val)
549563
{
550564
struct kvm_vcpu_smstateen_csr *csr = &vcpu->arch.smstateen_csr;
565+
unsigned long regs_max = sizeof(struct kvm_riscv_smstateen_csr) /
566+
sizeof(unsigned long);
551567

552-
if (reg_num >= sizeof(struct kvm_riscv_smstateen_csr) /
553-
sizeof(unsigned long))
568+
if (reg_num >= regs_max)
554569
return -EINVAL;
555570

571+
reg_num = array_index_nospec(reg_num, regs_max);
572+
556573
((unsigned long *)csr)[reg_num] = reg_val;
557574
return 0;
558575
}
@@ -562,11 +579,14 @@ static int kvm_riscv_vcpu_smstateen_get_csr(struct kvm_vcpu *vcpu,
562579
unsigned long *out_val)
563580
{
564581
struct kvm_vcpu_smstateen_csr *csr = &vcpu->arch.smstateen_csr;
582+
unsigned long regs_max = sizeof(struct kvm_riscv_smstateen_csr) /
583+
sizeof(unsigned long);
565584

566-
if (reg_num >= sizeof(struct kvm_riscv_smstateen_csr) /
567-
sizeof(unsigned long))
585+
if (reg_num >= regs_max)
568586
return -EINVAL;
569587

588+
reg_num = array_index_nospec(reg_num, regs_max);
589+
570590
*out_val = ((unsigned long *)csr)[reg_num];
571591
return 0;
572592
}

0 commit comments

Comments
 (0)