Skip to content

Commit e750f85

Browse files
committed
KVM: x86: Don't (re)check L1 intercepts when completing userspace I/O
When completing emulation of instruction that generated a userspace exit for I/O, don't recheck L1 intercepts as KVM has already finished that phase of instruction execution, i.e. has already committed to allowing L2 to perform I/O. If L1 (or host userspace) modifies the I/O permission bitmaps during the exit to userspace, KVM will treat the access as being intercepted despite already having emulated the I/O access. Pivot on EMULTYPE_NO_DECODE to detect that KVM is completing emulation. Of the three users of EMULTYPE_NO_DECODE, only complete_emulated_io() (the intended "recipient") can reach the code in question. gp_interception()'s use is mutually exclusive with is_guest_mode(), and complete_emulated_insn_gp() unconditionally pairs EMULTYPE_NO_DECODE with EMULTYPE_SKIP. The bad behavior was detected by a syzkaller program that toggles port I/O interception during the userspace I/O exit, ultimately resulting in a WARN on vcpu->arch.pio.count being non-zero due to KVM no completing emulation of the I/O instruction. WARNING: CPU: 23 PID: 1083 at arch/x86/kvm/x86.c:8039 emulator_pio_in_out+0x154/0x170 [kvm] Modules linked in: kvm_intel kvm irqbypass CPU: 23 UID: 1000 PID: 1083 Comm: repro Not tainted 6.16.0-rc5-c1610d2d66b1-next-vm #74 NONE Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:emulator_pio_in_out+0x154/0x170 [kvm] PKRU: 55555554 Call Trace: <TASK> kvm_fast_pio+0xd6/0x1d0 [kvm] vmx_handle_exit+0x149/0x610 [kvm_intel] kvm_arch_vcpu_ioctl_run+0xda8/0x1ac0 [kvm] kvm_vcpu_ioctl+0x244/0x8c0 [kvm] __x64_sys_ioctl+0x8a/0xd0 do_syscall_64+0x5d/0xc60 entry_SYSCALL_64_after_hwframe+0x4b/0x53 </TASK> Reported-by: syzbot+cc2032ba16cc2018ca25@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/68790db4.a00a0220.3af5df.0020.GAE@google.com Fixes: 8a76d7f ("KVM: x86: Add x86 callback for intercept check") Cc: stable@vger.kernel.org Cc: Jim Mattson <jmattson@google.com> Link: https://lore.kernel.org/r/20250715190638.1899116-1-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent c17b750 commit e750f85

File tree

3 files changed

+13
-14
lines changed

3 files changed

+13
-14
lines changed

arch/x86/kvm/emulate.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5107,12 +5107,11 @@ void init_decode_cache(struct x86_emulate_ctxt *ctxt)
51075107
ctxt->mem_read.end = 0;
51085108
}
51095109

5110-
int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
5110+
int x86_emulate_insn(struct x86_emulate_ctxt *ctxt, bool check_intercepts)
51115111
{
51125112
const struct x86_emulate_ops *ops = ctxt->ops;
51135113
int rc = X86EMUL_CONTINUE;
51145114
int saved_dst_type = ctxt->dst.type;
5115-
bool is_guest_mode = ctxt->ops->is_guest_mode(ctxt);
51165115

51175116
ctxt->mem_read.pos = 0;
51185117

@@ -5160,7 +5159,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
51605159
fetch_possible_mmx_operand(&ctxt->dst);
51615160
}
51625161

5163-
if (unlikely(is_guest_mode) && ctxt->intercept) {
5162+
if (unlikely(check_intercepts) && ctxt->intercept) {
51645163
rc = emulator_check_intercept(ctxt, ctxt->intercept,
51655164
X86_ICPT_PRE_EXCEPT);
51665165
if (rc != X86EMUL_CONTINUE)
@@ -5189,7 +5188,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
51895188
goto done;
51905189
}
51915190

5192-
if (unlikely(is_guest_mode) && (ctxt->d & Intercept)) {
5191+
if (unlikely(check_intercepts) && (ctxt->d & Intercept)) {
51935192
rc = emulator_check_intercept(ctxt, ctxt->intercept,
51945193
X86_ICPT_POST_EXCEPT);
51955194
if (rc != X86EMUL_CONTINUE)
@@ -5243,7 +5242,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
52435242

52445243
special_insn:
52455244

5246-
if (unlikely(is_guest_mode) && (ctxt->d & Intercept)) {
5245+
if (unlikely(check_intercepts) && (ctxt->d & Intercept)) {
52475246
rc = emulator_check_intercept(ctxt, ctxt->intercept,
52485247
X86_ICPT_POST_MEMACCESS);
52495248
if (rc != X86EMUL_CONTINUE)

arch/x86/kvm/kvm_emulate.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,6 @@ struct x86_emulate_ops {
235235
void (*set_nmi_mask)(struct x86_emulate_ctxt *ctxt, bool masked);
236236

237237
bool (*is_smm)(struct x86_emulate_ctxt *ctxt);
238-
bool (*is_guest_mode)(struct x86_emulate_ctxt *ctxt);
239238
int (*leave_smm)(struct x86_emulate_ctxt *ctxt);
240239
void (*triple_fault)(struct x86_emulate_ctxt *ctxt);
241240
int (*set_xcr)(struct x86_emulate_ctxt *ctxt, u32 index, u64 xcr);
@@ -521,7 +520,7 @@ bool x86_page_table_writing_insn(struct x86_emulate_ctxt *ctxt);
521520
#define EMULATION_RESTART 1
522521
#define EMULATION_INTERCEPTED 2
523522
void init_decode_cache(struct x86_emulate_ctxt *ctxt);
524-
int x86_emulate_insn(struct x86_emulate_ctxt *ctxt);
523+
int x86_emulate_insn(struct x86_emulate_ctxt *ctxt, bool check_intercepts);
525524
int emulator_task_switch(struct x86_emulate_ctxt *ctxt,
526525
u16 tss_selector, int idt_index, int reason,
527526
bool has_error_code, u32 error_code);

arch/x86/kvm/x86.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8470,11 +8470,6 @@ static bool emulator_is_smm(struct x86_emulate_ctxt *ctxt)
84708470
return is_smm(emul_to_vcpu(ctxt));
84718471
}
84728472

8473-
static bool emulator_is_guest_mode(struct x86_emulate_ctxt *ctxt)
8474-
{
8475-
return is_guest_mode(emul_to_vcpu(ctxt));
8476-
}
8477-
84788473
#ifndef CONFIG_KVM_SMM
84798474
static int emulator_leave_smm(struct x86_emulate_ctxt *ctxt)
84808475
{
@@ -8558,7 +8553,6 @@ static const struct x86_emulate_ops emulate_ops = {
85588553
.guest_cpuid_is_intel_compatible = emulator_guest_cpuid_is_intel_compatible,
85598554
.set_nmi_mask = emulator_set_nmi_mask,
85608555
.is_smm = emulator_is_smm,
8561-
.is_guest_mode = emulator_is_guest_mode,
85628556
.leave_smm = emulator_leave_smm,
85638557
.triple_fault = emulator_triple_fault,
85648558
.set_xcr = emulator_set_xcr,
@@ -9143,7 +9137,14 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
91439137
ctxt->exception.address = 0;
91449138
}
91459139

9146-
r = x86_emulate_insn(ctxt);
9140+
/*
9141+
* Check L1's instruction intercepts when emulating instructions for
9142+
* L2, unless KVM is re-emulating a previously decoded instruction,
9143+
* e.g. to complete userspace I/O, in which case KVM has already
9144+
* checked the intercepts.
9145+
*/
9146+
r = x86_emulate_insn(ctxt, is_guest_mode(vcpu) &&
9147+
!(emulation_type & EMULTYPE_NO_DECODE));
91479148

91489149
if (r == EMULATION_INTERCEPTED)
91499150
return 1;

0 commit comments

Comments
 (0)