mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations
@ 2026-10-29  2:40 Kevin Cheng
  0 siblings, 0 replies; only message in thread
From: Kevin Cheng @ 2026-10-29  2:40 UTC (permalink / raw)
  To: seanjc, pbonzini; +Cc: kvm, linux-kernel, yosry, Kevin Cheng

When synthesizing an EPT Violation to inject into L1 in response to a
fault detected while emulating an L2 GVA access, populate bits 11:9 of
EXIT_QUALIFICATION, i.e. the "advanced VM-Exit information", with the U/S,
R/W, and X/NX attributes of the L2 linear address' translation.

KVM has advertised VMX_EPT_ADVANCED_VMEXIT_INFO_BIT to L1 since
commit a0efd8cb221f ("KVM: nVMX: pass advanced EPT violation vmexit info
to guest"), but has never correctly populated bits 11:9 for synthesized
faults.  Originally the bits were pulled from vmcs02.EXIT_QUALIFICATION,
which is stale and/or holds unrelated data when the fault didn't come
from an EPT Violation VM-Exit.  Commit 96b067b59ad9 ("KVM: VMX:
Synthesize nested EPT violation GVA_IS_VALID/GVA_TRANSLATED bits")
correctly stopped consuming the stale vmcs02 data for synthesized faults,
but nothing in the synthesized path sets bits 11:9, and so L1 now always
observes a supervisor, read-only, executable translation for every
synthesized EPT Violation.  Per the SDM, bits 11:9 are valid whenever
bits 7 and 8 (GVA_IS_VALID and GVA_TRANSLATED) are set and advanced
VM-Exit information is supported.

Fill in the bits from vmx_translate_nested_gpa(), which already receives
the effective L2 pte_access (for MBEC), rather than from the EPT walker
in paging_tmpl.h: the L2 page attributes are not EPT properties and so
are not visible to the EPT walker, and the nonpaging L2 case (CR0.PG=0,
where every linear address is architecturally user-mode, writable, and
executable) is handled for free as nonpaging_gva_to_gpa() passes ACC_ALL.

Skip the synthesis for EPT Misconfigs, as the walker doesn't initialize
exit_qualification for misconfigs and the injection path ignores it, and
leave bits 11:9 clear when GVA_TRANSLATED is clear, as the bits are
architecturally undefined in that case.

Explicitly strip bits 11:9 from the synthesized fault's exit qualification
in nested_ept_inject_page_fault() when L1 isn't advertised advanced
VM-Exit information. Previously that was implicitly guaranteed because
nothing set the bits in the synthesized fault, but now that
vmx_translate_nested_gpa() does, relying on the bits being outside the
"take from the fault" mask would leak them to L1.

Fixes: a0efd8cb221f ("KVM: nVMX: pass advanced EPT violation vmexit info to guest")
Signed-off-by: Kevin Cheng <chengkev@google.com>
Assisted-by: LLM
---
Tested on hardware with advanced VM-Exit information support by running
the vmx kvm-unit-tests, including a new ept_access_test_adv_info test
(will be sent separately) that exercises bits 11:9 for both native
accesses, where the bits are propagated from vmcs02, and emulated accesses
(string I/O), where KVM must synthesize the bits.  The emulated cases fail
without this patch and pass with it.

Per Documentation/process/generated-content.rst: an LLM-based coding
assistant was used to help analyze the nested EPT fault-injection paths
and review the EXIT_QUALIFICATION bit handling.  The code and changelog
were written, reviewed, and tested by the author.

 arch/x86/kvm/mmu/paging_tmpl.h |  3 ++-
 arch/x86/kvm/vmx/nested.c      | 49 ++++++++++++++++++++++++++++++----
 2 files changed, 46 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h
index 8e350095508c..7023872b5ebb 100644
--- a/arch/x86/kvm/mmu/paging_tmpl.h
+++ b/arch/x86/kvm/mmu/paging_tmpl.h
@@ -508,7 +508,8 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
 	 *         out of date if it is serving an EPT misconfiguration.
 	 * [5:3] - Calculated by the page walk of the guest EPT page tables
 	 * [7:8] - Derived from "fault stage" access bits
-	 * [9:11] - Derived from [9:11] of real exit_qualification
+	 * [9:11] - Filled in by the caller (vmx_translate_nested_gpa()) based
+	 *          on the L2 page table walk, if the fault is synthesized.
 	 *
 	 * The other bits are set to 0.
 	 */
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 9b0bfa2f854c..f7cee9b3a7dd 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -452,15 +452,22 @@ static void nested_ept_inject_page_fault(struct kvm_vcpu *vcpu,
 			vm_exit_reason = EXIT_REASON_EPT_MISCONFIG;
 			exit_qualification = 0;
 		} else {
+			const u64 adv_info = EPT_VIOLATION_GVA_USER |
+					     EPT_VIOLATION_GVA_WRITABLE |
+					     EPT_VIOLATION_GVA_NX;
 			u64 mask = EPT_VIOLATION_GVA_IS_VALID |
 				   EPT_VIOLATION_GVA_TRANSLATED;
 
 			if (vmx->nested.msrs.ept_caps & VMX_EPT_ADVANCED_VMEXIT_INFO_BIT)
-				mask |= EPT_VIOLATION_GVA_USER |
-					EPT_VIOLATION_GVA_WRITABLE |
-					EPT_VIOLATION_GVA_NX;
+				mask |= adv_info;
 
-			exit_qualification = fault->exit_qualification & ~mask;
+			/*
+			 * Always drop the advanced VM-Exit information bits from
+			 * the synthesized fault, as they are undefined unless L1
+			 * is advertised support, in which case they'll be filled
+			 * in below from the appropriate source.
+			 */
+			exit_qualification = fault->exit_qualification & ~(mask | adv_info);
 
 			/*
 			 * Use the EXIT_QUALIFICATION from the VMCS if and only
@@ -7499,7 +7506,39 @@ static gpa_t vmx_translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa,
 	if ((pte_access & ACC_USER_MASK) && (access & PFERR_GUEST_FINAL_MASK))
 		access |= PFERR_USER_MASK;
 
-	return w->gva_to_gpa(vcpu, w, gpa, access, exception);
+	gpa = w->gva_to_gpa(vcpu, w, gpa, access, exception);
+	if (likely(gpa != INVALID_GPA) || !exception)
+		return gpa;
+
+	/*
+	 * Synthesize the "advanced VM-Exit information" bits for EPT
+	 * Violations that are the result of translating a linear address.
+	 * Bits 11:9 of EXIT_QUALIFICATION report the U/S, R/W, and X/NX
+	 * attributes of the linear address' translation, which are known only
+	 * to the caller walking L2's page tables, i.e. are not visible to the
+	 * EPT walker (nor should they be, they aren't EPT properties).
+	 *
+	 * Note, bits 11:9 are architecturally undefined if bit 8 is '0', i.e.
+	 * if the access wasn't to the final translation, and must be '0' if
+	 * L1 doesn't support advanced VM-Exit information (handled when the
+	 * fault is injected into L1).  Note #2, when L2 has paging disabled,
+	 * every linear address is a user-mode, writable, and executable
+	 * address, which is exactly what the nonpaging MMU passes in.  Lastly,
+	 * EPT Misconfigs don't have an EXIT_QUALIFICATION, don't touch it.
+	 */
+	if ((exception->error_code & PFERR_RSVD_MASK) ||
+	    !(exception->exit_qualification & EPT_VIOLATION_GVA_IS_VALID) ||
+	    !(exception->exit_qualification & EPT_VIOLATION_GVA_TRANSLATED))
+		return gpa;
+
+	if (pte_access & ACC_USER_MASK)
+		exception->exit_qualification |= EPT_VIOLATION_GVA_USER;
+	if (pte_access & ACC_WRITE_MASK)
+		exception->exit_qualification |= EPT_VIOLATION_GVA_WRITABLE;
+	if (!(pte_access & ACC_EXEC_MASK))
+		exception->exit_qualification |= EPT_VIOLATION_GVA_NX;
+
+	return gpa;
 }
 
 struct kvm_x86_nested_ops vmx_nested_ops __initdata = {
-- 
2.56.0.313.g2479322e68-goog


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-10-29  2:40 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-29  2:40 [PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations Kevin Cheng

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®