[RFC PATCH 09/55] KVM: x86: hyper-v: Let userspace fault a forwarded hypercall
From: David Woodhouse
Date: Sat Oct 17 2026 - 08:38:56 EST
From: David Woodhouse <dwmw@xxxxxxxxxxxx>
The architectural failure mode for an invalid VTL transition is #UD at
the VMCALL, not a hypercall status — a status would advance RIP and
complete the call. Define a result flag for the forwarded-hypercall
completion which queues #UD at the unadvanced RIP and ignores the rest
of the result.
Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
Assisted-by: LLM
---
arch/x86/kvm/hyperv.c | 14 +++++++++++++-
include/uapi/linux/kvm.h | 8 ++++++++
tools/include/uapi/linux/kvm.h | 9 +++++++++
3 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index ec669dafda6e..e41d101e327a 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -2473,7 +2473,19 @@ static int kvm_hv_hypercall_complete(struct kvm_vcpu *vcpu, u64 result)
static int kvm_hv_hypercall_complete_userspace(struct kvm_vcpu *vcpu)
{
- return kvm_hv_hypercall_complete(vcpu, vcpu->run->hyperv.u.hcall.result);
+ u64 result = vcpu->run->hyperv.u.hcall.result;
+
+ /*
+ * A hypercall the VMM judged malformed beyond returning a status
+ * (such as a VTL call that is invalid in the current state) faults
+ * at the instruction instead of completing.
+ */
+ if (result & KVM_HV_HCALL_RESULT_RAISE_UD) {
+ kvm_queue_exception(vcpu, UD_VECTOR);
+ return 1;
+ }
+
+ return kvm_hv_hypercall_complete(vcpu, result);
}
static int kvm_hvcall_signal_event(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 2aeb8c5172cb..b55e28be5181 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1016,6 +1016,14 @@ struct kvm_enable_cap {
#define KVM_CAP_S390_VSIE_ESAMODE 248
#define KVM_CAP_PLANES 249
#define KVM_CAP_HYPERV_FORWARD_HCALL 250
+
+/*
+ * Userspace completion of a forwarded Hyper-V hypercall (KVM_EXIT_HYPERV_HCALL
+ * under KVM_CAP_HYPERV_FORWARD_HCALL): setting this bit in the result makes
+ * KVM raise #UD at the faulting instruction instead of completing the
+ * hypercall; all other result bits are then ignored.
+ */
+#define KVM_HV_HCALL_RESULT_RAISE_UD (1ULL << 63)
#define KVM_CAP_HCALL_FAULT_EXIT 252
struct kvm_irq_routing_irqchip {
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h
index 8d9f7e6bba19..b55e28be5181 100644
--- a/tools/include/uapi/linux/kvm.h
+++ b/tools/include/uapi/linux/kvm.h
@@ -1017,6 +1017,15 @@ struct kvm_enable_cap {
#define KVM_CAP_PLANES 249
#define KVM_CAP_HYPERV_FORWARD_HCALL 250
+/*
+ * Userspace completion of a forwarded Hyper-V hypercall (KVM_EXIT_HYPERV_HCALL
+ * under KVM_CAP_HYPERV_FORWARD_HCALL): setting this bit in the result makes
+ * KVM raise #UD at the faulting instruction instead of completing the
+ * hypercall; all other result bits are then ignored.
+ */
+#define KVM_HV_HCALL_RESULT_RAISE_UD (1ULL << 63)
+#define KVM_CAP_HCALL_FAULT_EXIT 252
+
struct kvm_irq_routing_irqchip {
__u32 irqchip;
__u32 pin;
--
2.50.1