The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH] KVM: nVMX: Re-arm the vmcs12 pages request if mapping the pages fails
@ 2026-08-13  4:39 Jinwoo Lee
       [not found] ` <20260813045727.903981F000E9@smtp.kernel.org>
  2026-08-13 13:43 ` Sean Christopherson
  0 siblings, 2 replies; 4+ messages in thread
From: Jinwoo Lee @ 2026-08-13  4:39 UTC (permalink / raw)
  To: seanjc, pbonzini; +Cc: kvm, linux-kernel, Jinwoo Lee, stable

Re-arm KVM_REQ_GET_NESTED_STATE_PAGES when nested_get_vmcs12_pages()
fails, so that KVM retries the mapping on the next KVM_RUN instead of
resuming L2 with a stale vmcs02.

On failure KVM exits to userspace with KVM_EXIT_INTERNAL_ERROR but
leaves the vCPU in guest mode with vmcs02 loaded.  The request has
already been consumed by kvm_check_request() in vcpu_enter_guest(), and
nothing re-arms it, so a subsequent KVM_RUN goes straight to VM-Enter.

vmcs02's APIC_ACCESS_ADDR, VIRTUAL_APIC_PAGE_ADDR and
POSTED_INTR_DESC_ADDR still hold the host physical addresses that were
mapped for the previous nested VM-Enter.  Those pages have already been
unmapped and unpinned by nested_put_vmcs12_pages(), which runs after
vmx_switch_vmcs() to vmcs01 and therefore cannot clear the vmcs02
fields, and prepare_vmcs02_early() re-arms the controls that consume
them without rewriting the address fields.  Hardware then accesses
pages that KVM no longer holds a reference to.

The SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES branch makes this worse by
returning before the CPU_BASED_TPR_SHADOW and posted interrupt
fallbacks, which would otherwise write INVALID_GPA to
VIRTUAL_APIC_PAGE_ADDR and clear PIN_BASED_POSTED_INTR.

Commit 671ddc700fd0 ("KVM: nVMX: Don't leak L1 MMIO regions to L2")
replaced the "clear the control" fallback with an error return.  The
intent is right, but it left vmcs02 in a usable state.  Re-arming the
request closes that; if the mapping keeps failing KVM keeps exiting to
userspace, which is noisy but safe.

Note this relies on KVM_REQ_GET_NESTED_STATE_PAGES being cleared on
nested VM-Exit, which "KVM: nVMX: Ensure KVM_REQ_GET_NESTED_STATE_PAGES
is cleared on VM-Exit" makes unconditional.

Fixes: 671ddc700fd0 ("KVM: nVMX: Don't leak L1 MMIO regions to L2")
Cc: stable@vger.kernel.org
Signed-off-by: Jinwoo Lee <rkskek9254@gmail.com>
---
Notes for reviewers, not intended for the commit log.

Affected versions: v5.4-rc5 (671ddc700fd0) through v7.2-rc6.  Verified that
nested_get_vmcs12_pages() and the KVM_REQ_GET_NESTED_STATE_PAGES consumer in
vcpu_enter_guest() are unchanged in kvm-x86/next as of 2026-08-13.

Disclosure: this was found with AI-assisted code review, so per
Documentation/process/security-bugs.rst it is being reported publicly rather
than to security@kernel.org.

What I verified empirically, on the RSM path with the load_pdptrs() abort:

  - KVM consumes KVM_REQ_GET_NESTED_STATE_PAGES, the mapping fails, KVM_RUN
    returns 0 with run->exit_reason left at KVM_EXIT_UNKNOWN, and the vCPU is
    still in guest mode.
  - The request is not re-armed, and the next KVM_RUN VM-Enters L2, which then
    executes with vmcs02 still naming the previously mapped pages.  Confirmed
    deterministically (4/4) with a selftest, plus a control run showing that
    the same sequence without the poison maps successfully.
  - With the patch applied the code compiles clean, but I have not been able to
    boot a patched kernel, so the fix itself is not runtime tested.  The
    selftest fails on an unpatched kernel as expected.

What I did not verify:

  - The APIC-access branch end to end.  That is the interesting one, because it
    is reachable by L1 alone (point vmcs12->apic_access_addr at an unbacked
    GPA) and it returns before the CPU_BASED_TPR_SHADOW and posted interrupt
    fallbacks.  This host does not expose
    SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES or PIN_BASED_POSTED_INTR to L1, so
    I could only reach the load_pdptrs() abort, which needs userspace to poison
    the PDPTEs through a KVM_GUESTDBG_SINGLESTEP window and is therefore not
    guest-triggerable on its own.
  - Whether a stale page is actually reused by the host.  The selftest keeps
    every page allocated as its own guest RAM for the whole run.

On whether userspace resumes: QEMU's kvm_cpu_exec() treats
KVM_INTERNAL_ERROR_EMULATION as recoverable and returns EXCP_INTERRUPT when
kvm_arch_stop_on_emulation_error() is false, which for x86 is the case when the
guest is in protected mode at CPL 3 (target/i386/kvm/kvm.c).  It does not
re-push nested state on that path.  This is from reading qemu.git at
055952c0aa91; I have not run it.

A selftest is available.  I have not included it here per the reproducer
guidance in security-bugs.rst; happy to post it if you want it.

 arch/x86/kvm/vmx/nested.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index ddf6df7bee93..9e9bd6c541ba 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -3453,7 +3453,7 @@ static bool nested_get_vmcs12_pages(struct kvm_vcpu *vcpu)
 		 * state which can lead to a load of wrong PDPTRs.
 		 */
 		if (CC(!load_pdptrs(vcpu, vcpu->arch.cr3)))
-			return false;
+			goto fail;
 	}


@@ -3469,7 +3469,7 @@ static bool nested_get_vmcs12_pages(struct kvm_vcpu *vcpu)
 			vcpu->run->internal.suberror =
 				KVM_INTERNAL_ERROR_EMULATION;
 			vcpu->run->internal.ndata = 0;
-			return false;
+			goto fail;
 		}
 	}

@@ -3525,6 +3525,20 @@ static bool nested_get_vmcs12_pages(struct kvm_vcpu *vcpu)
 		exec_controls_clearbit(vmx, CPU_BASED_USE_MSR_BITMAPS);

 	return true;
+
+fail:
+	/*
+	 * Re-arm the request so that KVM retries the mapping instead of running
+	 * L2 with a stale vmcs02.  Bailing here leaves the vCPU in guest mode
+	 * with vmcs02 loaded and its APIC-access, virtual-APIC and posted
+	 * interrupt descriptor addresses still pointing at the host pages that
+	 * were mapped for the *previous* nested VM-Enter, which have since been
+	 * unmapped and unpinned by nested_put_vmcs12_pages().  KVM returns to
+	 * userspace without leaving guest mode, so if userspace resumes the
+	 * vCPU, VM-Enter succeeds and hardware accesses those stale HPAs.
+	 */
+	kvm_make_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu);
+	return false;
 }

 static bool vmx_get_nested_state_pages(struct kvm_vcpu *vcpu)
--
2.43.0

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

end of thread, other threads:[~2026-08-13 13:45 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13  4:39 [PATCH] KVM: nVMX: Re-arm the vmcs12 pages request if mapping the pages fails Jinwoo Lee
     [not found] ` <20260813045727.903981F000E9@smtp.kernel.org>
2026-08-13  8:10   ` Jinwoo Lee
2026-08-13 13:45     ` Sean Christopherson
2026-08-13 13:43 ` Sean Christopherson

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