All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kevin Cheng <chengkev@google.com>
To: seanjc@google.com, pbonzini@redhat.com
Cc: kvm@vger.kernel.org, linux-kernel@vger.kernel.org,
	yosry@kernel.org,  Kevin Cheng <chengkev@google.com>
Subject: [PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations
Date: Thu, 29 Oct 2026 02:40:05 +0000	[thread overview]
Message-ID: <20261029024005.575065-1-chengkev@google.com> (raw)

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


             reply	other threads:[~2026-10-29  2:40 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-29  2:40 Kevin Cheng [this message]
2026-10-29 15:17 ` [PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations Jim Mattson
2026-11-15 12:13 ` kernel test robot

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=20261029024005.575065-1-chengkev@google.com \
    --to=chengkev@google.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=seanjc@google.com \
    --cc=yosry@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.