From: Sean Christopherson <seanjc@google.com>
To: Oliver Upton <oupton@google.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>,
kvm@vger.kernel.org, Vitaly Kuznetsov <vkuznets@redhat.com>,
Wanpeng Li <wanpengli@tencent.com>,
Jim Mattson <jmattson@google.com>, Joerg Roedel <joro@8bytes.org>,
David Dunn <daviddunn@google.com>,
Peter Shier <pshier@google.com>
Subject: Re: [PATCH 1/2] KVM: x86: Allow userspace to opt out of hypercall patching
Date: Fri, 25 Mar 2022 23:53:05 +0000 [thread overview]
Message-ID: <Yj5V4adpnh8/B/K0@google.com> (raw)
In-Reply-To: <YjzBB6GzNGrJdRC2@google.com>
On Thu, Mar 24, 2022, Oliver Upton wrote:
> On Thu, Mar 24, 2022 at 06:57:18PM +0100, Paolo Bonzini wrote:
> > On 3/24/22 18:44, Sean Christopherson wrote:
> > > On Wed, Mar 16, 2022, Oliver Upton wrote:
> > > > KVM handles the VMCALL/VMMCALL instructions very strangely. Even though
> > > > both of these instructions really should #UD when executed on the wrong
> > > > vendor's hardware (i.e. VMCALL on SVM, VMMCALL on VMX), KVM replaces the
> > > > guest's instruction with the appropriate instruction for the vendor.
> > > > Nonetheless, older guest kernels without commit c1118b3602c2 ("x86: kvm:
> > > > use alternatives for VMCALL vs. VMMCALL if kernel text is read-only")
> > > > do not patch in the appropriate instruction using alternatives, likely
> > > > motivating KVM's intervention.
> > > >
> > > > Add a quirk allowing userspace to opt out of hypercall patching.
> > >
> > > A quirk may not be appropriate, per Paolo, the whole cross-vendor thing is
> > > intentional.
> > >
> > > https://lore.kernel.org/all/20211210222903.3417968-1-seanjc@google.com
> >
> > It's intentional, but the days of the patching part are over.
> >
> > KVM should just call emulate_hypercall if called with the wrong opcode
> > (which in turn can be quirked away). That however would be more complex to
> > implement because the hypercall path wants to e.g. inject a #UD with
> > kvm_queue_exception().
> >
> > All this makes me want to just apply Oliver's patch.
> >
> > > > + if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_FIX_HYPERCALL_INSN)) {
> > > > + ctxt->exception.error_code_valid = false;
> > > > + ctxt->exception.vector = UD_VECTOR;
> > > > + ctxt->have_exception = true;
> > > > + return X86EMUL_PROPAGATE_FAULT;
> > >
> > > This should return X86EMUL_UNHANDLEABLE instead of manually injecting a #UD. That
> > > will also end up generating a #UD in most cases, but will play nice with
> > > KVM_CAP_EXIT_ON_EMULATION_FAILURE.
>
> Sean and I were looking at this together right now, and it turns out
> that returning X86EMUL_UNHANDLEABLE at this point will unconditionally
> bounce out to userspace.
>
> x86_decode_emulated_instruction() would need to be the spot we bail to
> guard these exits with the CAP.
I've spent waaay too much time thinking about this...
TL;DR: I'm in favor of applying the patch as-is.
My objection to manually injecting the #UD is that there's no guarantee that KVM
is actually handling a #UD. It's largely a moot point, as KVM barfs on VMCALL/VMMCALL
if it's _not_ from a #UD, e.g. KVM hangs the guest if it does a VMCALL when KVM is
emulating due to lack of unrestricted guest. Forcing #UD is probably a net positive
in that case, as it will cause KVM to ultimately fail with "emulation error" and
bail to userspace.
It is possible to handle this in decode (idea below), but it will set us up for
pain later. If KVM ever does gain support for truly emulating hypercall, then as
Paolo said, the question of whether to emulate the "wrong" hypercall is orthogonal
to whether or not to patch. The correct way to check if the "wrong" hypercall
should be emulated would be to query the vCPU vendor via guest's CPUID, at which
point we _do_ want to get into em_hypercall() on UD to do the CPUID check even if
the quirk is disabled. So I agree with Paolo, make it a quirk that guards the
patching, and document that because of KVM's deficiencies, that also happens to
mean that encountering the "wrong" hypercall is fatal to the guest. Maybe fodder
for KVM's new vCPU errata? :-) If we fix that erratum, then the quirk can be
modified to only skip the patching.
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index be83c9c8482d..29abeaf605e2 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -5248,9 +5248,15 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int
ctxt->execute = opcode.u.execute;
- if (unlikely(emulation_type & EMULTYPE_TRAP_UD) &&
- likely(!(ctxt->d & EmulateOnUD)))
- return EMULATION_FAILED;
+ if (unlikely(emulation_type & EMULTYPE_TRAP_UD)) {
+ if (likely(!(ctxt->d & EmulateOnUD)))
+ return EMULATION_FAILED;
+
+ /* blah blah blah */
+ if (ctxt->execute == em_hypercall &&
+ !ctxt->has_quirk_fix_hypercall)
+ return EMULATION_FAILED;
+ }
if (unlikely(ctxt->d &
(NotImpl|Stack|Op3264|Sse|Mmx|Intercept|CheckPerm|NearBranch|
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
index 1cbd46cf71f9..35d2f20d368c 100644
--- a/arch/x86/kvm/kvm_emulate.h
+++ b/arch/x86/kvm/kvm_emulate.h
@@ -305,6 +305,8 @@ struct x86_emulate_ctxt {
void *vcpu;
const struct x86_emulate_ops *ops;
+ bool has_quirk_fix_hypercall;
+
/* Register state before/after emulation. */
unsigned long eflags;
unsigned long eip; /* eip before instruction emulation */
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 685c4bc453b4..832f85e72978 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7915,6 +7915,8 @@ static struct x86_emulate_ctxt *alloc_emulate_ctxt(struct kvm_vcpu *vcpu)
ctxt->vcpu = vcpu;
ctxt->ops = &emulate_ops;
+ ctxt->has_quirk_fix_hypercall =
+ kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_FIX_HYPERCALL_INSN);
vcpu->arch.emulate_ctxt = ctxt;
return ctxt;
@@ -9291,16 +9293,8 @@ static int emulator_fix_hypercall(struct x86_emulate_ctxt *ctxt)
char instruction[3];
unsigned long rip = kvm_rip_read(vcpu);
- /*
- * If the quirk is disabled, synthesize a #UD and let the guest pick up
- * the pieces.
- */
- if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_FIX_HYPERCALL_INSN)) {
- ctxt->exception.error_code_valid = false;
- ctxt->exception.vector = UD_VECTOR;
- ctxt->have_exception = true;
- return X86EMUL_PROPAGATE_FAULT;
- }
+ if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_FIX_HYPERCALL_INSN))
+ return X86EMUL_CONTINUE;
static_call(kvm_x86_patch_hypercall)(vcpu, instruction);
next prev parent reply other threads:[~2022-03-25 23:53 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-03-16 0:55 [PATCH 0/2] KVM: x86: Allow opt out of guest hypercall patching Oliver Upton
2022-03-16 0:55 ` [PATCH 1/2] KVM: x86: Allow userspace to opt out of " Oliver Upton
2022-03-24 17:44 ` Sean Christopherson
2022-03-24 17:57 ` Paolo Bonzini
2022-03-24 19:05 ` Oliver Upton
2022-03-25 23:53 ` Sean Christopherson [this message]
2022-03-28 17:28 ` Oliver Upton
2022-03-28 18:28 ` Sean Christopherson
2022-08-24 9:34 ` Maxim Levitsky
2022-08-24 14:43 ` Sean Christopherson
2022-08-24 15:06 ` Maxim Levitsky
2022-08-24 17:15 ` Paolo Bonzini
2022-08-24 18:40 ` Sean Christopherson
2022-03-16 0:55 ` [PATCH 2/2] selftests: KVM: Test KVM_X86_QUIRK_FIX_HYPERCALL_INSN Oliver Upton
2022-03-24 19:09 ` Oliver Upton
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Yj5V4adpnh8/B/K0@google.com \
--to=seanjc@google.com \
--cc=daviddunn@google.com \
--cc=jmattson@google.com \
--cc=joro@8bytes.org \
--cc=kvm@vger.kernel.org \
--cc=oupton@google.com \
--cc=pbonzini@redhat.com \
--cc=pshier@google.com \
--cc=vkuznets@redhat.com \
--cc=wanpengli@tencent.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox