Re: [PATCH] KVM: nVMX: Synthesize advanced VM-Exit info for emulated EPT Violations
From: Jim Mattson
Date: Thu Oct 29 2026 - 11:20:40 EST
On Wed, Oct 28, 2026 at 7:40 PM Kevin Cheng <chengkev@xxxxxxxxxx> 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@xxxxxxxxxx>
> 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.