|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v2 5/7] x86/p2m: invalidate EPT caches after flushing a nested p2m
From: Chunjie Zhu <chunjie.zhu@xxxxxxxxxx>
p2m_flush_table_locked() keeps the np2m root page, so the EP4TA is
unchanged, clears it, and returns the remaining page-table pages to the
paging pool. The only invalidation it triggers is
nestedhvm_vmcx_flushtlb(), whose IPI handler does hvm_asid_flush_core()
and drops the cached nv_p2m.
That only flushes VPID/ASID-tagged translations. EPT paging-structure
caches and guest-physical mappings are tagged by EP4TA and survive.
After the flush they can still point at the freed np2m pages.
vmx_vmenter_helper() issues INVEPT only if the CPU is set in
hostp2m->ept.invalidate. ept_sync_domain_prepare() sets that mask only
when an already-present host p2m entry changes. Nested flushes (L1
INVEPT, np2m LRU recycle, p2m_flush()) never set it. Filling non-present
np2m entries afterwards doesn't set it either.
Once those freed pages are reused, or scrubbed (0xc2 pattern on debug
builds), L2 walks through stale cached pointers and can fetch or read
garbage. In practice this showed up as L2 guests executing 0xc2c2c2c2.
Mark every CPU in the host p2m's ept.invalidate mask at the end of
p2m_flush_table_locked(), so each CPU does an INVEPT on its next VM
entry. The cost is paid only when a flush actually happens.
Signed-off-by: Chunjie Zhu <chunjie.zhu@xxxxxxxxxx>
---
xen/arch/x86/mm/p2m.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
index 7660ea48fc64..77e3d9b57574 100644
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -1425,6 +1425,17 @@ p2m_flush_table_locked(struct p2m_domain *p2m)
d->arch.paging.free_page(d, pg);
}
page_list_add(top, &p2m->pages);
+
+ /*
+ * The root page is kept (so the EP4TA is unchanged) and the other tables
+ * are freed for reuse, but INVVPID/ASID flushes don't cover the EPT
+ * paging-structure caches or guest-physical mappings, which are tagged by
+ * EP4TA. Without an INVEPT, a walk can follow a cached pointer into a
+ * freed (possibly reused or scrubbed) table. vmx_vmenter_helper() does
+ * the INVEPT on every pCPU's next VM entry.
+ */
+ if ( cpu_has_vmx && nestedhvm_enabled(d) )
+ cpumask_setall(p2m_get_hostp2m(d)->ept.invalidate);
}
/* Reset this p2m table to be empty */
--
2.34.1
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |