* [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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox