Skip to content

Commit 653ea44

Browse files
committed
KVM: nVMX: Honor userspace MSR filter lists for nested VM-Enter/VM-Exit
Synthesize a consistency check VM-Exit (VM-Enter) or VM-Abort (VM-Exit) if L1 attempts to load/store an MSR via the VMCS MSR lists that userspace has disallowed access to via an MSR filter. Intel already disallows including a handful of "special" MSRs in the VMCS lists, so denying access isn't completely without precedent. More importantly, the behavior is well-defined _and_ can be communicated the end user, e.g. to the customer that owns a VM running as L1 on top of KVM. On the other hand, ignoring userspace MSR filters is all but guaranteed to result in unexpected behavior as the access will hit KVM's internal state, which is likely not up-to-date. Unlike KVM-internal accesses, instruction emulation, and dedicated VMCS fields, the MSRs in the VMCS load/store lists are 100% guest controlled, thus making it all but impossible to reason about the correctness of ignoring the MSR filter. And if userspace *really* wants to deny access to MSRs via the aforementioned scenarios, userspace can hide the associated feature from the guest, e.g. by disabling the PMU to prevent accessing PERF_GLOBAL_CTRL via its VMCS field. But for the MSR lists, KVM is blindly processing MSRs; the MSR filters are the _only_ way for userspace to deny access. This partially reverts commit ac8d6ca ("KVM: x86: Only do MSR filtering when access MSR by rdmsr/wrmsr"). Cc: Hou Wenlong <[email protected]> Cc: Jim Mattson <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sean Christopherson <[email protected]>
1 parent d9aa56e commit 653ea44

File tree

4 files changed

+31
-12
lines changed

4 files changed

+31
-12
lines changed

Documentation/virt/kvm/api.rst

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4214,7 +4214,9 @@ whether or not KVM_CAP_X86_USER_SPACE_MSR's KVM_MSR_EXIT_REASON_FILTER is
42144214
enabled. If KVM_MSR_EXIT_REASON_FILTER is enabled, KVM will exit to userspace
42154215
on denied accesses, i.e. userspace effectively intercepts the MSR access. If
42164216
KVM_MSR_EXIT_REASON_FILTER is not enabled, KVM will inject a #GP into the guest
4217-
on denied accesses.
4217+
on denied accesses. Note, if an MSR access is denied during emulation of MSR
4218+
load/stores during VMX transitions, KVM ignores KVM_MSR_EXIT_REASON_FILTER.
4219+
See the below warning for full details.
42184220

42194221
If an MSR access is allowed by userspace, KVM will emulate and/or virtualize
42204222
the access in accordance with the vCPU model. Note, KVM may still ultimately
@@ -4229,9 +4231,22 @@ filtering. In that mode, ``KVM_MSR_FILTER_DEFAULT_DENY`` is invalid and causes
42294231
an error.
42304232

42314233
.. warning::
4232-
MSR accesses as part of nested VM-Enter/VM-Exit are not filtered.
4233-
This includes both writes to individual VMCS fields and reads/writes
4234-
through the MSR lists pointed to by the VMCS.
4234+
MSR accesses that are side effects of instruction execution (emulated or
4235+
native) are not filtered as hardware does not honor MSR bitmaps outside of
4236+
RDMSR and WRMSR, and KVM mimics that behavior when emulating instructions
4237+
to avoid pointless divergence from hardware. E.g. RDPID reads MSR_TSC_AUX,
4238+
SYSENTER reads the SYSENTER MSRs, etc.
4239+
4240+
MSRs that are loaded/stored via dedicated VMCS fields are not filtered as
4241+
part of VM-Enter/VM-Exit emulation.
4242+
4243+
MSRs that are loaded/store via VMX's load/store lists _are_ filtered as part
4244+
of VM-Enter/VM-Exit emulation. If an MSR access is denied on VM-Enter, KVM
4245+
synthesizes a consistency check VM-Exit(EXIT_REASON_MSR_LOAD_FAIL). If an
4246+
MSR access is denied on VM-Exit, KVM synthesizes a VM-Abort. In short, KVM
4247+
extends Intel's architectural list of MSRs that cannot be loaded/saved via
4248+
the VM-Enter/VM-Exit MSR list. It is platform owner's responsibility to
4249+
to communicate any such restrictions to their end users.
42354250

42364251
x2APIC MSR accesses cannot be filtered (KVM silently ignores filters that
42374252
cover any x2APIC MSRs).

arch/x86/include/asm/kvm_host.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,6 +2060,8 @@ void kvm_prepare_emulation_failure_exit(struct kvm_vcpu *vcpu);
20602060

20612061
void kvm_enable_efer_bits(u64);
20622062
bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer);
2063+
int kvm_get_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 *data);
2064+
int kvm_set_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 data);
20632065
int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, bool host_initiated);
20642066
int kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data);
20652067
int kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data);

arch/x86/kvm/vmx/nested.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,7 @@ static u32 nested_vmx_load_msr(struct kvm_vcpu *vcpu, u64 gpa, u32 count)
981981
__func__, i, e.index, e.reserved);
982982
goto fail;
983983
}
984-
if (kvm_set_msr(vcpu, e.index, e.value)) {
984+
if (kvm_set_msr_with_filter(vcpu, e.index, e.value)) {
985985
pr_debug_ratelimited(
986986
"%s cannot write MSR (%u, 0x%x, 0x%llx)\n",
987987
__func__, i, e.index, e.value);
@@ -1017,7 +1017,7 @@ static bool nested_vmx_get_vmexit_msr_value(struct kvm_vcpu *vcpu,
10171017
}
10181018
}
10191019

1020-
if (kvm_get_msr(vcpu, msr_index, data)) {
1020+
if (kvm_get_msr_with_filter(vcpu, msr_index, data)) {
10211021
pr_debug_ratelimited("%s cannot read MSR (0x%x)\n", __func__,
10221022
msr_index);
10231023
return false;
@@ -1112,9 +1112,9 @@ static void prepare_vmx_msr_autostore_list(struct kvm_vcpu *vcpu,
11121112
/*
11131113
* Emulated VMEntry does not fail here. Instead a less
11141114
* accurate value will be returned by
1115-
* nested_vmx_get_vmexit_msr_value() using kvm_get_msr()
1116-
* instead of reading the value from the vmcs02 VMExit
1117-
* MSR-store area.
1115+
* nested_vmx_get_vmexit_msr_value() by reading KVM's
1116+
* internal MSR state instead of reading the value from
1117+
* the vmcs02 VMExit MSR-store area.
11181118
*/
11191119
pr_warn_ratelimited(
11201120
"Not enough msr entries in msr_autostore. Can't add msr %x\n",
@@ -4806,7 +4806,7 @@ static void nested_vmx_restore_host_state(struct kvm_vcpu *vcpu)
48064806
goto vmabort;
48074807
}
48084808

4809-
if (kvm_set_msr(vcpu, h.index, h.value)) {
4809+
if (kvm_set_msr_with_filter(vcpu, h.index, h.value)) {
48104810
pr_debug_ratelimited(
48114811
"%s WRMSR failed (%u, 0x%x, 0x%llx)\n",
48124812
__func__, j, h.index, h.value);

arch/x86/kvm/x86.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1940,19 +1940,21 @@ static int kvm_get_msr_ignored_check(struct kvm_vcpu *vcpu,
19401940
return ret;
19411941
}
19421942

1943-
static int kvm_get_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 *data)
1943+
int kvm_get_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 *data)
19441944
{
19451945
if (!kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_READ))
19461946
return KVM_MSR_RET_FILTERED;
19471947
return kvm_get_msr_ignored_check(vcpu, index, data, false);
19481948
}
1949+
EXPORT_SYMBOL_GPL(kvm_get_msr_with_filter);
19491950

1950-
static int kvm_set_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 data)
1951+
int kvm_set_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 data)
19511952
{
19521953
if (!kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_WRITE))
19531954
return KVM_MSR_RET_FILTERED;
19541955
return kvm_set_msr_ignored_check(vcpu, index, data, false);
19551956
}
1957+
EXPORT_SYMBOL_GPL(kvm_set_msr_with_filter);
19561958

19571959
int kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data)
19581960
{

0 commit comments

Comments
 (0)