All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations
@ 2026-10-29  2:40 Kevin Cheng
  2026-10-29 15:17 ` Jim Mattson
  2026-11-15 12:13 ` kernel test robot
  0 siblings, 2 replies; 3+ messages 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 related	[flat|nested] 3+ messages in thread

* Re: [PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations
  2026-10-29  2:40 [PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations Kevin Cheng
@ 2026-10-29 15:17 ` Jim Mattson
  2026-11-15 12:13 ` kernel test robot
  1 sibling, 0 replies; 3+ messages in thread
From: Jim Mattson @ 2026-10-29 15:17 UTC (permalink / raw)
  To: Kevin Cheng; +Cc: seanjc, pbonzini, kvm, linux-kernel, yosry

On Wed, Oct 28, 2026 at 7:40 PM Kevin Cheng <chengkev@google.com> wrote:
>
> 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.

Doesn't this break tools/testing/selftests/kvm/x86/nested_tdp_fault_test.c
on hardware that supports VMX_EPT_ADVANCED_VMEXIT_INFO_BIT?

In that test, l2_test_page is mapped writable in L2's page tables via
vm_alloc(), and l1_vmx_code() asserts exact equality on EXIT_QUALIFICATION
without expecting EPT_VIOLATION_GVA_WRITABLE in TEST_FINAL_PAGE_UNMAPPED
and TEST_FINAL_PAGE_WRITE_PROTECTED.

Also, rather than only adding a test to kvm-unit-tests, please update and
extend nested_tdp_fault_test.c to cover the advanced VM-Exit information
bits.

[ ... ]

> -                       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);

Why set these bits in vmx_translate_nested_gpa() when
VMX_EPT_ADVANCED_VMEXIT_INFO_BIT isn't set in vmx->nested.msrs.ept_caps,
only to strip them out here?

If you gate the synthesis in vmx_translate_nested_gpa() on
VMX_EPT_ADVANCED_VMEXIT_INFO_BIT, then nested_ept_inject_page_fault()
doesn't need to be touched at all (or better yet, the mask dance in
nested_ept_inject_page_fault() can be confined to the 'if (from_hardware)'
block.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations
  2026-10-29  2:40 [PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations Kevin Cheng
  2026-10-29 15:17 ` Jim Mattson
@ 2026-11-15 12:13 ` kernel test robot
  1 sibling, 0 replies; 3+ messages in thread
From: kernel test robot @ 2026-11-15 12:13 UTC (permalink / raw)
  To: Kevin Cheng
  Cc: oe-lkp, lkp, kvm, seanjc, pbonzini, linux-kernel, yosry,
	Kevin Cheng, yi1.lai


Hello,

kernel test robot noticed "kselftests.kvm.nested_tdp_fault_test.fail" on:

commit: 1a01388ba3bd52d1535b10bb5974059f9418cd9e ("[PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations")
url: https://github.com/intel-lab-lkp/linux/commits/Kevin-Cheng/KVM-nVMX-Synthesize-advanced-VM-Exit-info-for-emulated-EPT-Violations/20261029-024005
base: https://git.kernel.org/cgit/virt/kvm/kvm.git queue patch link: https://lore.kernel.org/all/20261029024005.575065-1-chengkev@google.com/
patch subject: [PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations

in testcase: kselftests
version: kselftests-x86_64-05441d697f7f-1_20261105
with following parameters:

	group: kvm

config: x86_64-rhel-9.4-kselftests
compiler: gcc-14
test machine: 224 threads 2 sockets Intel(R) Xeon(R) Platinum 8480CTDX (Sapphire Rapids) with 256G memory

(please refer to attached dmesg/kmsg for entire log/backtrace)


If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <yi1.lai@intel.com>
| Closes: https://lore.kernel.org/oe-lkp/202611151806.4ddbcfeb-lkp@intel.com

# selftests: kvm: nested_tdp_fault_test
# Random seed: 0x3ccb3ded
#
# ==== Test Assertion Failure ====
#   x86/nested_tdp_fault_test.c:84: (exit_qual) == (((((1UL))) << (0)) | ((((1UL))) << (7)) | ((((1UL))) << (8)))
#   pid=10529 tid=10529 errno=4 - Interrupted system call
#      1        0x0000000000413921: assert_on_unhandled_exception at processor.c:786
#      2        0x00000000004075e6: _vcpu_run at kvm_util.c:1770
#      3         (inlined by) vcpu_run at kvm_util.c:1781
#      4        0x0000000000403902: run_test at nested_tdp_fault_test.c:275
#      5        0x0000000000402c09: main at nested_tdp_fault_test.c:302
#      6        0x00007f102953aca7: ?? ??:0
#      7        0x00007f102953ad64: ?? ??:0
#      8        0x0000000000402d50: _start at ??:?
#   Wanted EXIT_QUAL '0x181', got '0x581'
not ok 40 selftests: kvm: nested_tdp_fault_test # exit=254


The kernel config and materials to reproduce are available at:
https://download.01.org/0day-ci/archive/20261115/202611151806.4ddbcfeb-lkp@intel.com


--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-11-15 12:14 UTC | newest]

Thread overview: 3+ messages (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
2026-10-29 15:17 ` Jim Mattson
2026-11-15 12:13 ` kernel test robot

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.