|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen stable-4.3] VMX: fix cr0.cd handling
commit 064275dcfb6002dc4327ba94b6bd758806735538
Author: Liu Jinsong <jinsong.liu@xxxxxxxxx>
AuthorDate: Mon Dec 9 14:27:04 2013 +0100
Commit: Jan Beulich <jbeulich@xxxxxxxx>
CommitDate: Mon Dec 9 14:27:04 2013 +0100
VMX: fix cr0.cd handling
This patch solves XSA-60 security hole:
1. For guest w/o VT-d, and for guest with VT-d but snooped, Xen need
do nothing, since hardware snoop mechanism has ensured cache coherency.
2. For guest with VT-d but non-snooped, cache coherency can not be
guaranteed by h/w snoop, therefore it need emulate UC type to guest:
2.1). if it works w/ Intel EPT, set guest IA32_PAT fields as UC so that
guest memory type are all UC.
2.2). if it works w/ shadow, drop all shadows so that any new ones would
be created on demand w/ UC.
This patch also fix a bug of shadow cr0.cd setting. Current shadow has a
small window between cache flush and TLB invalidation, resulting in possilbe
cache pollution. This patch pause vcpus so that no vcpus context involved
into the window.
This is CVE-2013-2212 / XSA-60.
Signed-off-by: Liu Jinsong <jinsong.liu@xxxxxxxxx>
Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx>
Reviewed-by: Tim Deegan <tim@xxxxxxx>
Reviewed-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
Acked-by: Jun Nakajima <jun.nakajima@xxxxxxxxx>
Acked-by: Keir Fraser <keir@xxxxxxx>
master commit: 62652c00efa55fb45374bcc92f7d96fc411aebb2
master date: 2013-11-06 10:12:36 +0100
---
xen/arch/x86/hvm/hvm.c | 75 ++++++++++++++++++++++--------------
xen/arch/x86/hvm/vmx/vmcs.c | 2 +-
xen/arch/x86/hvm/vmx/vmx.c | 54 +++++++++++++++++++++++++-
xen/common/domain.c | 10 +++++
xen/include/asm-x86/hvm/hvm.h | 1 +
xen/include/asm-x86/hvm/support.h | 2 +
xen/include/xen/sched.h | 1 +
7 files changed, 113 insertions(+), 32 deletions(-)
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index ce2a9d8..e9cd4a7 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -1711,6 +1711,40 @@ int hvm_mov_from_cr(unsigned int cr, unsigned int gpr)
return X86EMUL_UNHANDLEABLE;
}
+void hvm_shadow_handle_cd(struct vcpu *v, unsigned long value)
+{
+ if ( value & X86_CR0_CD )
+ {
+ /* Entering no fill cache mode. */
+ spin_lock(&v->domain->arch.hvm_domain.uc_lock);
+ v->arch.hvm_vcpu.cache_mode = NO_FILL_CACHE_MODE;
+
+ if ( !v->domain->arch.hvm_domain.is_in_uc_mode )
+ {
+ domain_pause_nosync(v->domain);
+
+ /* Flush physical caches. */
+ on_each_cpu(local_flush_cache, NULL, 1);
+ hvm_set_uc_mode(v, 1);
+
+ domain_unpause(v->domain);
+ }
+ spin_unlock(&v->domain->arch.hvm_domain.uc_lock);
+ }
+ else if ( !(value & X86_CR0_CD) &&
+ (v->arch.hvm_vcpu.cache_mode == NO_FILL_CACHE_MODE) )
+ {
+ /* Exit from no fill cache mode. */
+ spin_lock(&v->domain->arch.hvm_domain.uc_lock);
+ v->arch.hvm_vcpu.cache_mode = NORMAL_CACHE_MODE;
+
+ if ( domain_exit_uc_mode(v) )
+ hvm_set_uc_mode(v, 0);
+
+ spin_unlock(&v->domain->arch.hvm_domain.uc_lock);
+ }
+}
+
int hvm_set_cr0(unsigned long value)
{
struct vcpu *v = current;
@@ -1793,35 +1827,18 @@ int hvm_set_cr0(unsigned long value)
}
}
- if ( cache_flush_permitted(v->domain) )
- {
- if ( (value & X86_CR0_CD) && !(value & X86_CR0_NW) )
- {
- /* Entering no fill cache mode. */
- spin_lock(&v->domain->arch.hvm_domain.uc_lock);
- v->arch.hvm_vcpu.cache_mode = NO_FILL_CACHE_MODE;
-
- if ( !v->domain->arch.hvm_domain.is_in_uc_mode )
- {
- /* Flush physical caches. */
- on_each_cpu(local_flush_cache, NULL, 1);
- hvm_set_uc_mode(v, 1);
- }
- spin_unlock(&v->domain->arch.hvm_domain.uc_lock);
- }
- else if ( !(value & (X86_CR0_CD | X86_CR0_NW)) &&
- (v->arch.hvm_vcpu.cache_mode == NO_FILL_CACHE_MODE) )
- {
- /* Exit from no fill cache mode. */
- spin_lock(&v->domain->arch.hvm_domain.uc_lock);
- v->arch.hvm_vcpu.cache_mode = NORMAL_CACHE_MODE;
-
- if ( domain_exit_uc_mode(v) )
- hvm_set_uc_mode(v, 0);
-
- spin_unlock(&v->domain->arch.hvm_domain.uc_lock);
- }
- }
+ /*
+ * When cr0.cd setting
+ * 1. For guest w/o VT-d, and for guest with VT-d but snooped, Xen need not
+ * do anything, since hardware snoop mechanism has ensured cache coherency;
+ * 2. For guest with VT-d but non-snooped, cache coherency cannot be
+ * guaranteed by h/w so need emulate UC memory type to guest.
+ */
+ if ( ((value ^ old_value) & X86_CR0_CD) &&
+ has_arch_pdevs(v->domain) &&
+ iommu_enabled && !iommu_snoop &&
+ hvm_funcs.handle_cd )
+ hvm_funcs.handle_cd(v, value);
v->arch.hvm_vcpu.guest_cr[0] = value;
hvm_update_guest_cr(v, 0);
diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c
index c824494..7e74fba 100644
--- a/xen/arch/x86/hvm/vmx/vmcs.c
+++ b/xen/arch/x86/hvm/vmx/vmcs.c
@@ -920,7 +920,7 @@ static int construct_vmcs(struct vcpu *v)
vmx_disable_intercept_for_msr(v, MSR_IA32_SYSENTER_CS, MSR_TYPE_R |
MSR_TYPE_W);
vmx_disable_intercept_for_msr(v, MSR_IA32_SYSENTER_ESP, MSR_TYPE_R |
MSR_TYPE_W);
vmx_disable_intercept_for_msr(v, MSR_IA32_SYSENTER_EIP, MSR_TYPE_R |
MSR_TYPE_W);
- if ( paging_mode_hap(d) )
+ if ( paging_mode_hap(d) && (!iommu_enabled || iommu_snoop) )
vmx_disable_intercept_for_msr(v, MSR_IA32_CR_PAT, MSR_TYPE_R |
MSR_TYPE_W);
}
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index 9f98252..32751d2 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -923,7 +923,8 @@ static unsigned long vmx_get_shadow_gs_base(struct vcpu *v)
static int vmx_set_guest_pat(struct vcpu *v, u64 gpat)
{
- if ( !paging_mode_hap(v->domain) )
+ if ( !paging_mode_hap(v->domain) ||
+ unlikely(v->arch.hvm_vcpu.cache_mode == NO_FILL_CACHE_MODE) )
return 0;
vmx_vmcs_enter(v);
@@ -934,7 +935,8 @@ static int vmx_set_guest_pat(struct vcpu *v, u64 gpat)
static int vmx_get_guest_pat(struct vcpu *v, u64 *gpat)
{
- if ( !paging_mode_hap(v->domain) )
+ if ( !paging_mode_hap(v->domain) ||
+ unlikely(v->arch.hvm_vcpu.cache_mode == NO_FILL_CACHE_MODE) )
return 0;
vmx_vmcs_enter(v);
@@ -943,6 +945,53 @@ static int vmx_get_guest_pat(struct vcpu *v, u64 *gpat)
return 1;
}
+static void vmx_handle_cd(struct vcpu *v, unsigned long value)
+{
+ if ( !paging_mode_hap(v->domain) )
+ {
+ /*
+ * For shadow, 'load IA32_PAT' VM-entry control is 0, so it cannot
+ * set guest memory type as UC via IA32_PAT. Xen drop all shadows
+ * so that any new ones would be created on demand.
+ */
+ hvm_shadow_handle_cd(v, value);
+ }
+ else
+ {
+ u64 *pat = &v->arch.hvm_vcpu.pat_cr;
+
+ if ( value & X86_CR0_CD )
+ {
+ /*
+ * For EPT, set guest IA32_PAT fields as UC so that guest
+ * memory type are all UC.
+ */
+ u64 uc_pat =
+ ((uint64_t)PAT_TYPE_UNCACHABLE) | /* PAT0 */
+ ((uint64_t)PAT_TYPE_UNCACHABLE << 8) | /* PAT1 */
+ ((uint64_t)PAT_TYPE_UNCACHABLE << 16) | /* PAT2 */
+ ((uint64_t)PAT_TYPE_UNCACHABLE << 24) | /* PAT3 */
+ ((uint64_t)PAT_TYPE_UNCACHABLE << 32) | /* PAT4 */
+ ((uint64_t)PAT_TYPE_UNCACHABLE << 40) | /* PAT5 */
+ ((uint64_t)PAT_TYPE_UNCACHABLE << 48) | /* PAT6 */
+ ((uint64_t)PAT_TYPE_UNCACHABLE << 56); /* PAT7 */
+
+ vmx_get_guest_pat(v, pat);
+ vmx_set_guest_pat(v, uc_pat);
+
+ wbinvd(); /* flush possibly polluted cache */
+ hvm_asid_flush_vcpu(v); /* invalidate memory type cached in TLB */
+ v->arch.hvm_vcpu.cache_mode = NO_FILL_CACHE_MODE;
+ }
+ else
+ {
+ v->arch.hvm_vcpu.cache_mode = NORMAL_CACHE_MODE;
+ vmx_set_guest_pat(v, *pat);
+ hvm_asid_flush_vcpu(v); /* no need to flush cache */
+ }
+ }
+}
+
static void vmx_set_tsc_offset(struct vcpu *v, u64 offset)
{
vmx_vmcs_enter(v);
@@ -1553,6 +1602,7 @@ static struct hvm_function_table __initdata
vmx_function_table = {
.msr_read_intercept = vmx_msr_read_intercept,
.msr_write_intercept = vmx_msr_write_intercept,
.invlpg_intercept = vmx_invlpg_intercept,
+ .handle_cd = vmx_handle_cd,
.set_info_guest = vmx_set_info_guest,
.set_rdtsc_exiting = vmx_set_rdtsc_exiting,
.nhvm_vcpu_initialise = nvmx_vcpu_initialise,
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 2612949..df17ece 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -808,6 +808,16 @@ void domain_pause(struct domain *d)
vcpu_sleep_sync(v);
}
+void domain_pause_nosync(struct domain *d)
+{
+ struct vcpu *v;
+
+ atomic_inc(&d->pause_count);
+
+ for_each_vcpu( d, v )
+ vcpu_sleep_nosync(v);
+}
+
void domain_unpause(struct domain *d)
{
struct vcpu *v;
diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h
index be52ef2..144ba6a 100644
--- a/xen/include/asm-x86/hvm/hvm.h
+++ b/xen/include/asm-x86/hvm/hvm.h
@@ -156,6 +156,7 @@ struct hvm_function_table {
int (*msr_read_intercept)(unsigned int msr, uint64_t *msr_content);
int (*msr_write_intercept)(unsigned int msr, uint64_t msr_content);
void (*invlpg_intercept)(unsigned long vaddr);
+ void (*handle_cd)(struct vcpu *v, unsigned long value);
void (*set_info_guest)(struct vcpu *v);
void (*set_rdtsc_exiting)(struct vcpu *v, bool_t);
diff --git a/xen/include/asm-x86/hvm/support.h
b/xen/include/asm-x86/hvm/support.h
index 7ddc806..52aef1f 100644
--- a/xen/include/asm-x86/hvm/support.h
+++ b/xen/include/asm-x86/hvm/support.h
@@ -130,6 +130,8 @@ void hvm_rdtsc_intercept(struct cpu_user_regs *regs);
int __must_check hvm_handle_xsetbv(u32 index, u64 new_bv);
+void hvm_shadow_handle_cd(struct vcpu *v, unsigned long value);
+
/* These functions all return X86EMUL return codes. */
int hvm_set_efer(uint64_t value);
int hvm_set_cr0(unsigned long value);
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index ae6a3b8..0ba9178 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -687,6 +687,7 @@ void vcpu_unblock(struct vcpu *v);
void vcpu_pause(struct vcpu *v);
void vcpu_pause_nosync(struct vcpu *v);
void domain_pause(struct domain *d);
+void domain_pause_nosync(struct domain *d);
void vcpu_unpause(struct vcpu *v);
void domain_unpause(struct domain *d);
void domain_pause_by_systemcontroller(struct domain *d);
--
generated by git-patchbot for /home/xen/git/xen.git#stable-4.3
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |