[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH V1 6/8] x86/p2m: invalidate EPT caches after flushing a nested p2m


  • To: xen-devel@xxxxxxxxxxxxxxxxxxxx
  • From: chunjie.zhu@xxxxxxxxxx
  • Date: Mon, 26 Oct 2026 15:17:52 +0800
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=citrix.com; dmarc=pass action=none header.from=citrix.com; dkim=pass header.d=citrix.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=4Rjw15GNe9uAxxOV8gF02IelZ4k42KeUNkkvV94GvCw=; b=ZLH3Fz1BAuVuubniLB5GyDMm9P/Y1TtBVgGdQpr9j9i3/k+Ng2sbU46zq7H5rdYZOFnix4bOAWtGaJNXcJlCOV3j/HDwXcTm+V7HkOSwM99gb/Ff3HoGdllGMuxYQP7c1PiqgaCZq5PFC5JOVrhk7SWmPS9JKmO04o7F31Y4TyyDOjoEtGquVMolA0tgaYUQzAqJnntF7AH46zoKi7wkaaga7bRRUMo+oMXCYa8tjXJ8SL6aMNpJOHDSakQsP+S1UhHkxzGZ2LUxQF5E83cg+nOXxhHYhnK3Uy5W4U82zuAZ6lRQgcUVzbu7NNT9LC9G6THFLgG2eNhb4aLsCeZTZQ==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=A8Bnc+2Gk4aQreutl8m8oIcOUiHcId4Y2w9jxQ8/aMLN7218DWDieXqkLZA0dUuucmNvrfvS3jvEd77LoS/cPvmTV994LBwDv5EQjzTE65OYoyBPHm1bgUC1FJxy30j2e3AL7ty+mfLMWdFgTsxmgcpA+5hmQH1n655YRS9SIsgHC1FcEo3Lrvj4BuThR53IalSMm3sA2NRMfpH5+FVTT7+etDIbAs/kSYQoFWwFrOQbsX2BuVF8uokIfQF0H0644cC6c1NwDf1JrtQetkZsuo0wdp+eobQ0pdeIdfCCGGjmNmFlJGnCeuO8WoQF1sY2HHVteHhyb7PWkXEad1oUCw==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=citrix.com header.i="@citrix.com" header.h="From:Date:Subject:Message-Id:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Authentication-results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=citrix.com;
  • Cc: andrew.cooper3@xxxxxxxxxx, anthony.perard@xxxxxxxxxx, michal.orzel@xxxxxxx, jbeulich@xxxxxxxx, julien@xxxxxxx, roger@xxxxxxxxxxxxxx, sstabellini@xxxxxxxxxx, dpsmith@xxxxxxxxxxxxxxxxxxxx, Chunjie Zhu <chunjie.zhu@xxxxxxxxxx>
  • Delivery-date: Mon, 26 Oct 2026 07:19:04 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

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 ae13d36fdab7..fbaa69d4f43f 100644
--- a/xen/arch/x86/mm/p2m.c
+++ b/xen/arch/x86/mm/p2m.c
@@ -1446,6 +1446,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




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.