Commit 2dda6a9e authored by Lukas Gerlach's avatar Lukas Gerlach Committed by Anup Patel
Browse files

KVM: riscv: Fix Spectre-v1 in PMU counter access



Guest-controlled counter indices received via SBI ecalls are used to
index into the PMC array. Sanitize them with array_index_nospec()
to prevent speculative out-of-bounds access.

Similar to x86 commit 13c5183a ("KVM: x86: Protect MSR-based
index computations in pmu.h from Spectre-v1/L1TF attacks").

Fixes: 8f0153ec ("RISC-V: KVM: Add skeleton support for perf")
Reviewed-by: default avatarRadim Krčmář <radim.krcmar@oss.qualcomm.com>
Signed-off-by: default avatarLukas Gerlach <lukas.gerlach@cispa.de>
Link: https://lore.kernel.org/r/20260303-kvm-riscv-spectre-v1-v2-4-192caab8e0dc@cispa.de


Signed-off-by: default avatarAnup Patel <anup@brainfault.org>
parent 8f0c15c4
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/kvm_host.h>
#include <linux/nospec.h>
#include <linux/perf/riscv_pmu.h>
#include <asm/csr.h>
#include <asm/kvm_vcpu_sbi.h>
@@ -87,7 +88,8 @@ static void kvm_pmu_release_perf_event(struct kvm_pmc *pmc)

static u64 kvm_pmu_get_perf_event_hw_config(u32 sbi_event_code)
{
	return hw_event_perf_map[sbi_event_code];
	return hw_event_perf_map[array_index_nospec(sbi_event_code,
						    SBI_PMU_HW_GENERAL_MAX)];
}

static u64 kvm_pmu_get_perf_event_cache_config(u32 sbi_event_code)
@@ -218,6 +220,7 @@ static int pmu_fw_ctr_read_hi(struct kvm_vcpu *vcpu, unsigned long cidx,
		return -EINVAL;
	}

	cidx = array_index_nospec(cidx, RISCV_KVM_MAX_COUNTERS);
	pmc = &kvpmu->pmc[cidx];

	if (pmc->cinfo.type != SBI_PMU_CTR_TYPE_FW)
@@ -244,6 +247,7 @@ static int pmu_ctr_read(struct kvm_vcpu *vcpu, unsigned long cidx,
		return -EINVAL;
	}

	cidx = array_index_nospec(cidx, RISCV_KVM_MAX_COUNTERS);
	pmc = &kvpmu->pmc[cidx];

	if (pmc->cinfo.type == SBI_PMU_CTR_TYPE_FW) {
@@ -525,6 +529,7 @@ int kvm_riscv_vcpu_pmu_ctr_info(struct kvm_vcpu *vcpu, unsigned long cidx,
		return 0;
	}

	cidx = array_index_nospec(cidx, RISCV_KVM_MAX_COUNTERS);
	retdata->out_val = kvpmu->pmc[cidx].cinfo.value;

	return 0;
@@ -559,7 +564,8 @@ int kvm_riscv_vcpu_pmu_ctr_start(struct kvm_vcpu *vcpu, unsigned long ctr_base,
	}
	/* Start the counters that have been configured and requested by the guest */
	for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) {
		pmc_index = i + ctr_base;
		pmc_index = array_index_nospec(i + ctr_base,
					       RISCV_KVM_MAX_COUNTERS);
		if (!test_bit(pmc_index, kvpmu->pmc_in_use))
			continue;
		/* The guest started the counter again. Reset the overflow status */
@@ -630,7 +636,8 @@ int kvm_riscv_vcpu_pmu_ctr_stop(struct kvm_vcpu *vcpu, unsigned long ctr_base,

	/* Stop the counters that have been configured and requested by the guest */
	for_each_set_bit(i, &ctr_mask, RISCV_MAX_COUNTERS) {
		pmc_index = i + ctr_base;
		pmc_index = array_index_nospec(i + ctr_base,
					       RISCV_KVM_MAX_COUNTERS);
		if (!test_bit(pmc_index, kvpmu->pmc_in_use))
			continue;
		pmc = &kvpmu->pmc[pmc_index];
@@ -761,6 +768,7 @@ int kvm_riscv_vcpu_pmu_ctr_cfg_match(struct kvm_vcpu *vcpu, unsigned long ctr_ba
		}
	}

	ctr_idx = array_index_nospec(ctr_idx, RISCV_KVM_MAX_COUNTERS);
	pmc = &kvpmu->pmc[ctr_idx];
	pmc->idx = ctr_idx;