From: Maxim Levitsky <mlevitsk@redhat.com>
To: kvm@vger.kernel.org
Cc: Paolo Bonzini <pbonzini@redhat.com>,
Ingo Molnar <mingo@redhat.com>, "H. Peter Anvin" <hpa@zytor.com>,
x86@kernel.org, Sean Christopherson <seanjc@google.com>,
Borislav Petkov <bp@alien8.de>,
linux-kernel@vger.kernel.org,
Dave Hansen <dave.hansen@linux.intel.com>,
Thomas Gleixner <tglx@linutronix.de>,
Maxim Levitsky <mlevitsk@redhat.com>
Subject: [PATCH v2 2/2] KVM: VMX: disable preemption when touching segment fields
Date: Mon, 15 Jul 2024 22:20:14 -0400 [thread overview]
Message-ID: <20240716022014.240960-3-mlevitsk@redhat.com> (raw)
In-Reply-To: <20240716022014.240960-1-mlevitsk@redhat.com>
VMX code uses segment cache to avoid reading guest segment fields.
The cache is reset each time a segment's field (e.g base/access rights/etc)
is written, and then a new value of this field is written.
However if the vCPU is preempted between these two events, and this
segment field is read (e.g kvm reads SS's access rights to check
if the vCPU is in kernel mode), then old field value will get
cached and never updated.
Usually a lock is required to avoid such race but since vCPU segments
are only accessed by its vCPU thread, we can avoid a lock and
only disable preemption, in places where the segment cache
is invalidated and segment fields are updated.
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
---
arch/x86/kvm/vmx/nested.c | 4 +++-
arch/x86/kvm/vmx/vmx.c | 25 +++++++++++++++++++------
arch/x86/kvm/vmx/vmx.h | 14 +++++++++++++-
3 files changed, 35 insertions(+), 8 deletions(-)
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index d3ca1a772ae67..b6597fe5d011d 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -2470,7 +2470,7 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12)
if (!hv_evmcs || !(hv_evmcs->hv_clean_fields &
HV_VMX_ENLIGHTENED_CLEAN_FIELD_GUEST_GRP2)) {
- vmx_segment_cache_clear(vmx);
+ vmx_write_segment_cache_start(vmx);
vmcs_write16(GUEST_ES_SELECTOR, vmcs12->guest_es_selector);
vmcs_write16(GUEST_CS_SELECTOR, vmcs12->guest_cs_selector);
@@ -2508,6 +2508,8 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12)
vmcs_writel(GUEST_TR_BASE, vmcs12->guest_tr_base);
vmcs_writel(GUEST_GDTR_BASE, vmcs12->guest_gdtr_base);
vmcs_writel(GUEST_IDTR_BASE, vmcs12->guest_idtr_base);
+
+ vmx_write_segment_cache_end(vmx);
}
if (!hv_evmcs || !(hv_evmcs->hv_clean_fields &
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index fa9f307d9b18b..26a5efd34aef7 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -2171,12 +2171,14 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
break;
#ifdef CONFIG_X86_64
case MSR_FS_BASE:
- vmx_segment_cache_clear(vmx);
+ vmx_write_segment_cache_start(vmx);
vmcs_writel(GUEST_FS_BASE, data);
+ vmx_write_segment_cache_end(vmx);
break;
case MSR_GS_BASE:
- vmx_segment_cache_clear(vmx);
+ vmx_write_segment_cache_start(vmx);
vmcs_writel(GUEST_GS_BASE, data);
+ vmx_write_segment_cache_end(vmx);
break;
case MSR_KERNEL_GS_BASE:
vmx_write_guest_kernel_gs_base(vmx, data);
@@ -3088,7 +3090,7 @@ static void enter_rmode(struct kvm_vcpu *vcpu)
vmx->rmode.vm86_active = 1;
- vmx_segment_cache_clear(vmx);
+ vmx_write_segment_cache_start(vmx);
vmcs_writel(GUEST_TR_BASE, kvm_vmx->tss_addr);
vmcs_write32(GUEST_TR_LIMIT, RMODE_TSS_SIZE - 1);
@@ -3109,6 +3111,8 @@ static void enter_rmode(struct kvm_vcpu *vcpu)
fix_rmode_seg(VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]);
fix_rmode_seg(VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]);
fix_rmode_seg(VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]);
+
+ vmx_write_segment_cache_end(vmx);
}
int vmx_set_efer(struct kvm_vcpu *vcpu, u64 efer)
@@ -3139,8 +3143,9 @@ int vmx_set_efer(struct kvm_vcpu *vcpu, u64 efer)
static void enter_lmode(struct kvm_vcpu *vcpu)
{
u32 guest_tr_ar;
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
- vmx_segment_cache_clear(to_vmx(vcpu));
+ vmx_write_segment_cache_start(vmx);
guest_tr_ar = vmcs_read32(GUEST_TR_AR_BYTES);
if ((guest_tr_ar & VMX_AR_TYPE_MASK) != VMX_AR_TYPE_BUSY_64_TSS) {
@@ -3150,6 +3155,9 @@ static void enter_lmode(struct kvm_vcpu *vcpu)
(guest_tr_ar & ~VMX_AR_TYPE_MASK)
| VMX_AR_TYPE_BUSY_64_TSS);
}
+
+ vmx_write_segment_cache_end(vmx);
+
vmx_set_efer(vcpu, vcpu->arch.efer | EFER_LMA);
}
@@ -3571,7 +3579,7 @@ void __vmx_set_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg)
struct vcpu_vmx *vmx = to_vmx(vcpu);
const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
- vmx_segment_cache_clear(vmx);
+ vmx_write_segment_cache_start(vmx);
if (vmx->rmode.vm86_active && seg != VCPU_SREG_LDTR) {
vmx->rmode.segs[seg] = *var;
@@ -3601,6 +3609,8 @@ void __vmx_set_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg)
var->type |= 0x1; /* Accessed */
vmcs_write32(sf->ar_bytes, vmx_segment_access_rights(var));
+
+ vmx_write_segment_cache_end(vmx);
}
void vmx_set_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg)
@@ -4870,7 +4880,8 @@ void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
vmx->hv_deadline_tsc = -1;
kvm_set_cr8(vcpu, 0);
- vmx_segment_cache_clear(vmx);
+ vmx_write_segment_cache_start(vmx);
+
kvm_register_mark_available(vcpu, VCPU_EXREG_SEGMENTS);
seg_setup(VCPU_SREG_CS);
@@ -4899,6 +4910,8 @@ void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
vmcs_writel(GUEST_IDTR_BASE, 0);
vmcs_write32(GUEST_IDTR_LIMIT, 0xffff);
+ vmx_write_segment_cache_end(vmx);
+
vmcs_write32(GUEST_ACTIVITY_STATE, GUEST_ACTIVITY_ACTIVE);
vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, 0);
vmcs_writel(GUEST_PENDING_DBG_EXCEPTIONS, 0);
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index 1689f0d59f435..cba14911032cd 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -755,9 +755,21 @@ static inline bool vmx_can_use_ipiv(struct kvm_vcpu *vcpu)
return lapic_in_kernel(vcpu) && enable_ipiv;
}
-static inline void vmx_segment_cache_clear(struct vcpu_vmx *vmx)
+static inline void vmx_write_segment_cache_start(struct vcpu_vmx *vmx)
+{
+ /* VMX segment cache can be accessed during preemption.
+ * (e.g to determine the guest's CPL)
+ *
+ * To avoid caching a wrong value during such access, disable
+ * the preemption
+ */
+ preempt_disable();
+}
+
+static inline void vmx_write_segment_cache_end(struct vcpu_vmx *vmx)
{
vmx->segment_cache.bitmask = 0;
+ preempt_enable();
}
#endif /* __KVM_X86_VMX_H */
--
2.26.3
next prev parent reply other threads:[~2024-07-16 2:20 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-16 2:20 [PATCH v2 0/2] Fix for a very old KVM bug in the segment cache Maxim Levitsky
2024-07-16 2:20 ` [PATCH v2 1/2] KVM: nVMX: use vmx_segment_cache_clear Maxim Levitsky
2024-07-16 21:07 ` Sean Christopherson
2024-07-24 18:18 ` Maxim Levitsky
2024-07-16 2:20 ` Maxim Levitsky [this message]
2024-07-16 22:36 ` [PATCH v2 2/2] KVM: VMX: disable preemption when touching segment fields Sean Christopherson
2024-07-25 12:59 ` Maxim Levitsky
2024-07-25 17:37 ` Maxim Levitsky
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240716022014.240960-3-mlevitsk@redhat.com \
--to=mlevitsk@redhat.com \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=hpa@zytor.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=pbonzini@redhat.com \
--cc=seanjc@google.com \
--cc=tglx@linutronix.de \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox