* [PATCH v2] KVM: x86: Restrict saved GPA writes to hardware write faults
@ 2026-08-28 23:58 Kyle Zeng
2026-08-29 0:09 ` sashiko-bot
0 siblings, 1 reply; 3+ messages in thread
From: Kyle Zeng @ 2026-08-28 23:58 UTC (permalink / raw)
To: kvm
Cc: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, Chris Ayoub, Kyle Zeng,
Oleg Boiko
A GPA supplied by a hardware page fault describes the access that
faulted, not an arbitrary instruction decoded afterwards. A guest can
change an MMIO read into a store before KVM fetches the instruction. The
saved-GPA shortcut then skips the write-aware guest page-table walk and
can issue a write through a guest-read-only mapping.
Carry hardware write-fault information into the emulator and retain it
alongside the saved GPA. Only reuse that GPA for a write when hardware
reported a write access. Reads and faults with unknown access direction
do not authorize an emulated write; fall back to the existing
permission-aware translation in those cases.
Keep the authorization across MMIO completion without decoding a new
instruction, and reset it when initializing a fresh emulator context.
This also covers writes reached through the cmpxchg fallback.
Preserve the shortcut for hardware-reported writes and for the read side
of read-modify-write emulation after such a fault. In particular, legacy
SEV guests can have encrypted page tables that KVM cannot walk, so forcing
those accesses through a software walk is not suitable.
Fixes: 0f89b207b04a ("kvm: svm: Use the hardware provided GPA instead of page walk")
Reported-by: Oleg Boiko <oboiko@openai.com>
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Kyle Zeng <kylebot@openai.com>
---
Changes in v2:
- Track saved-GPA access with an explicit access mask.
- Allow saved-GPA writes for any direct hardware write fault.
- Preserve read access on write faults for RMW emulation.
arch/x86/kvm/kvm_emulate.h | 4 ++--
arch/x86/kvm/mmu/mmu.c | 3 +++
arch/x86/kvm/x86.c | 14 +++++++++++---
arch/x86/kvm/x86.h | 3 +++
4 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
index 3e375af15c03..10886bea82c9 100644
--- a/arch/x86/kvm/kvm_emulate.h
+++ b/arch/x86/kvm/kvm_emulate.h
@@ -354,8 +354,8 @@ struct x86_emulate_ctxt {
bool have_exception;
struct x86_exception exception;
- /* GPA available */
- bool gpa_available;
+ /* Saved GPA and permitted emulated accesses. */
+ u64 gpa_access;
gpa_t gpa_val;
/*
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 064ecc33b926..95b9eac9962a 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -6632,6 +6632,9 @@ int noinline kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 err
return r;
emulate:
+ if (direct && (error_code & PFERR_WRITE_MASK))
+ emulation_type |= EMULTYPE_PF_WRITE;
+
return x86_emulate_instruction(vcpu, cr2_or_gpa, emulation_type, insn,
insn_len);
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 79468ddfe473..aa5deb7c73aa 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5088,8 +5088,13 @@ static int emulator_read_write_onepage(unsigned long addr, void *val,
* Note, this cannot be used on string operations since string
* operation using rep will only have the initial GPA from the NPF
* occurred.
+ *
+ * The guest may have changed the instruction since the fault. Reuse
+ * the GPA for writes only if hardware reported a write access;
+ * otherwise, recheck permissions for the decoded access.
*/
- if (ctxt->gpa_available && emulator_can_use_gpa(ctxt) &&
+ if ((ctxt->gpa_access & (write ? ACC_WRITE_MASK : ACC_READ_MASK)) &&
+ emulator_can_use_gpa(ctxt) &&
(addr & ~PAGE_MASK) == (ctxt->gpa_val & ~PAGE_MASK)) {
gpa = ctxt->gpa_val;
ret = vcpu_is_mmio_gpa(vcpu, addr, gpa, write);
@@ -5938,7 +5943,7 @@ static void init_emulate_ctxt(struct kvm_vcpu *vcpu)
kvm_x86_call(get_cs_db_l_bits)(vcpu, &cs_db, &cs_l);
- ctxt->gpa_available = false;
+ ctxt->gpa_access = 0;
ctxt->eflags = kvm_get_rflags(vcpu);
ctxt->tf = (ctxt->eflags & X86_EFLAGS_TF) != 0;
@@ -6454,7 +6459,10 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
/* With shadow page tables, cr2 contains a GVA or nGPA. */
if (vcpu->arch.mmu->root_role.direct) {
- ctxt->gpa_available = true;
+ /* A write fault can require a read for RMW emulation. */
+ ctxt->gpa_access = ACC_READ_MASK;
+ if (emulation_type & EMULTYPE_PF_WRITE)
+ ctxt->gpa_access |= ACC_WRITE_MASK;
ctxt->gpa_val = cr2_or_gpa;
}
} else {
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index 0f5919b092e4..667465680ccd 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -407,6 +407,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
* EMULTYPE_PF - Set when an intercepted #PF triggers the emulation, in which case
* the CR2/GPA value pass on the stack is valid.
*
+ * EMULTYPE_PF_WRITE - Set with EMULTYPE_PF when hardware reports a write access.
+ *
* EMULTYPE_COMPLETE_USER_EXIT - Set when the emulator should update interruptibility
* state and inject single-step #DBs after skipping
* an instruction (after completing userspace I/O).
@@ -445,6 +447,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
#define EMULTYPE_COMPLETE_USER_EXIT (1 << 7)
#define EMULTYPE_WRITE_PF_TO_SP (1 << 8)
#define EMULTYPE_SKIP_SOFT_INT (1 << 9)
+#define EMULTYPE_PF_WRITE (1 << 10)
#define EMULTYPE_SET_SOFT_INT_VECTOR(v) ((u32)((v) & 0xff) << 16)
#define EMULTYPE_GET_SOFT_INT_VECTOR(e) (((e) >> 16) & 0xff)
base-commit: 73e3f0710014fe6d4ed98cfc02292f6121db7558
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v2] KVM: x86: Restrict saved GPA writes to hardware write faults
2026-08-28 23:58 [PATCH v2] KVM: x86: Restrict saved GPA writes to hardware write faults Kyle Zeng
@ 2026-08-29 0:09 ` sashiko-bot
2026-09-01 20:41 ` Sean Christopherson
0 siblings, 1 reply; 3+ messages in thread
From: sashiko-bot @ 2026-08-29 0:09 UTC (permalink / raw)
To: Kyle Zeng; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Read-modify-write (RMW) instructions on unmapped MMIO pages will crash SEV guests because the emulated write phase lacks authorization, forcing an impossible software page table walk.
--
commit 2459e3d7bfb5a32b95970716f7650879e8b5d707
Author: Kyle Zeng <kylebot@openai.com>
KVM: x86: Restrict saved GPA writes to hardware write faults
This patch modifies KVM's instruction emulator to carry hardware write-fault
information and restricts the reuse of a saved GPA for writes only when the
hardware reported a write access. It introduces an access mask to authorize
writes based on the initial hardware fault or fall back to a full
translation if unauthorized.
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 79468ddfe4736..aa5deb7c73aa2 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -5088,8 +5088,13 @@ static int emulator_read_write_onepage(unsigned long addr, void *val,
> * Note, this cannot be used on string operations since string
> * operation using rep will only have the initial GPA from the NPF
> * occurred.
> + *
> + * The guest may have changed the instruction since the fault. Reuse
> + * the GPA for writes only if hardware reported a write access;
> + * otherwise, recheck permissions for the decoded access.
> */
> - if (ctxt->gpa_available && emulator_can_use_gpa(ctxt) &&
> + if ((ctxt->gpa_access & (write ? ACC_WRITE_MASK : ACC_READ_MASK)) &&
> + emulator_can_use_gpa(ctxt) &&
[Severity: High]
Will this break the write phase of read-modify-write (RMW) instructions for
SEV guests?
If a SEV guest executes an RMW instruction (like OR [mem], reg) on an
unmapped MMIO page, the hardware triggers a Nested Page Fault (NPF) on the
initial read access. KVM will set gpa_access to ACC_READ_MASK.
When the emulator attempts the write phase, it appears this check will fail
because ACC_WRITE_MASK is not set. This causes emulator_read_write_onepage()
to fall back to vcpu_mmio_gva_to_gpa().
Does this fallback attempt a software page table walk that will fail on
encrypted guest page tables, resulting in a spurious #PF and crashing the
SEV guest?
> (addr & ~PAGE_MASK) == (ctxt->gpa_val & ~PAGE_MASK)) {
> gpa = ctxt->gpa_val;
> ret = vcpu_is_mmio_gpa(vcpu, addr, gpa, write);
[ ... ]
> @@ -6454,7 +6459,10 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
>
> /* With shadow page tables, cr2 contains a GVA or nGPA. */
> if (vcpu->arch.mmu->root_role.direct) {
> - ctxt->gpa_available = true;
> + /* A write fault can require a read for RMW emulation. */
> + ctxt->gpa_access = ACC_READ_MASK;
> + if (emulation_type & EMULTYPE_PF_WRITE)
> + ctxt->gpa_access |= ACC_WRITE_MASK;
[Severity: High]
Does this limit gpa_access too strictly for read faults on RMW instructions?
As mentioned above, if an instruction naturally faults on its first (read)
access, it will only get ACC_READ_MASK here. This seems to be the root cause
that later prevents the write phase from reusing the GPA during MMIO
emulation.
> ctxt->gpa_val = cr2_or_gpa;
> }
> } else {
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260828235840.63227-1-kylebot@openai.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v2] KVM: x86: Restrict saved GPA writes to hardware write faults
2026-08-29 0:09 ` sashiko-bot
@ 2026-09-01 20:41 ` Sean Christopherson
0 siblings, 0 replies; 3+ messages in thread
From: Sean Christopherson @ 2026-09-01 20:41 UTC (permalink / raw)
To: sashiko-reviews; +Cc: Kyle Zeng, kvm
On Sat, Aug 29, 2026, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> > diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> > index 79468ddfe4736..aa5deb7c73aa2 100644
> > --- a/arch/x86/kvm/x86.c
> > +++ b/arch/x86/kvm/x86.c
> > @@ -5088,8 +5088,13 @@ static int emulator_read_write_onepage(unsigned long addr, void *val,
> > * Note, this cannot be used on string operations since string
> > * operation using rep will only have the initial GPA from the NPF
> > * occurred.
> > + *
> > + * The guest may have changed the instruction since the fault. Reuse
> > + * the GPA for writes only if hardware reported a write access;
> > + * otherwise, recheck permissions for the decoded access.
Eh, I would just omit this comment, because a RMW operation is far more likely
than the guest playing TLB games.
> > */
> > - if (ctxt->gpa_available && emulator_can_use_gpa(ctxt) &&
> > + if ((ctxt->gpa_access & (write ? ACC_WRITE_MASK : ACC_READ_MASK)) &&
> > + emulator_can_use_gpa(ctxt) &&
>
> [Severity: High]
> Will this break the write phase of read-modify-write (RMW) instructions for
> SEV guests?
>
> If a SEV guest executes an RMW instruction (like OR [mem], reg) on an
> unmapped MMIO page, the hardware triggers a Nested Page Fault (NPF) on the
> initial read access. KVM will set gpa_access to ACC_READ_MASK.
>
> When the emulator attempts the write phase, it appears this check will fail
> because ACC_WRITE_MASK is not set. This causes emulator_read_write_onepage()
> to fall back to vcpu_mmio_gva_to_gpa().
>
> Does this fallback attempt a software page table walk that will fail on
> encrypted guest page tables, resulting in a spurious #PF and crashing the
> SEV guest?
Ugh, SEV. Part of me wants to take an erratum for the whole mess, but that would
be quite gross since the behavior would be conditional for non-SEV guests. And
it should be easy enough to make this work for just SEV guests.
>
> > (addr & ~PAGE_MASK) == (ctxt->gpa_val & ~PAGE_MASK)) {
> > gpa = ctxt->gpa_val;
> > ret = vcpu_is_mmio_gpa(vcpu, addr, gpa, write);
>
> [ ... ]
>
> > @@ -6454,7 +6459,10 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
> >
> > /* With shadow page tables, cr2 contains a GVA or nGPA. */
> > if (vcpu->arch.mmu->root_role.direct) {
> > - ctxt->gpa_available = true;
> > + /* A write fault can require a read for RMW emulation. */
Heh, my brain fart notwithstanding, just drop this comment. Because while RMW
operations are why KVM needs to get this correct, it's doesn't explain why it's
correct to allow reads on write faults. We could write a comment for that, but
I think anyone that cares enough will be able to quickly deduce that reads are
allowed because x86 paging doesn't support write-only mappings.
> > + ctxt->gpa_access = ACC_READ_MASK;
> > + if (emulation_type & EMULTYPE_PF_WRITE)
> > + ctxt->gpa_access |= ACC_WRITE_MASK;
>
> [Severity: High]
> Does this limit gpa_access too strictly for read faults on RMW instructions?
>
> As mentioned above, if an instruction naturally faults on its first (read)
> access, it will only get ACC_READ_MASK here. This seems to be the root cause
> that later prevents the write phase from reusing the GPA during MMIO
> emulation.
For !SEV, that's fine. In practice, I highly doubt RMW operations are used in
fast paths, e.g. Linux-as-a-guest straight up supports only MOV instructions.
For SEV, this as fixup? And then if someone cares enough (I don't think I care?),
we could add an entry in Documentation/virt/kvm/x86/errata.rst for SEV.
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 3db88742ea9a..4f6659aa72ec 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -6459,9 +6459,17 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
/* With shadow page tables, cr2 contains a GVA or nGPA. */
if (vcpu->arch.mmu->root_role.direct) {
- /* A write fault can require a read for RMW emulation. */
ctxt->gpa_access = ACC_READ_MASK;
- if (emulation_type & EMULTYPE_PF_WRITE)
+ /*
+ * Always allow writes for SEV guests, as the guest's
+ * page tables are encrypted, i.e. KVM can't walk the
+ * guest's page tables and so must always use the GPA
+ * from the initial fault. Restricting use of the GPA
+ * to the access type that faulted would prevent KVM
+ * from emulating RMW operations for SEV guests.
+ */
+ if ((emulation_type & EMULTYPE_PF_WRITE) ||
+ is_sev_guest(vcpu))
ctxt->gpa_access |= ACC_WRITE_MASK;
ctxt->gpa_val = cr2_or_gpa;
}
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-01 20:41 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-28 23:58 [PATCH v2] KVM: x86: Restrict saved GPA writes to hardware write faults Kyle Zeng
2026-08-29 0:09 ` sashiko-bot
2026-09-01 20:41 ` Sean Christopherson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox