Re: [PATCH 2/3] KVM: x86: Reject enabling KVM_CAP_X86_BUS_LOCK_EXIT when !kvm_caps.has_bus_lock_exit

From: Sean Christopherson

Date: Thu Aug 06 2026 - 10:36:29 EST


On Thu, Aug 06, 2026, Xiaoyao Li wrote:
> Return -EINVAL to reject the enabling of KVM_CAP_X86_BUS_LOCK_EXIT from
> userspace when kvm_caps.has_bus_lock_exit is false.
>
> For KVM_BUS_LOCK_DETECTION_EXIT, if KVM doesn't support BUS LOCK EXIT,
> return error to userspace instead of success.
>
> For KVM_BUS_LOCK_DETECTION_OFF, it seems OK to allow it when KVM doesn't
> support bus_lock_exit. But from an API perspective, it implies
> inconsistency that KVM_CAP_X86_BUS_LOCK_EXIT reports 0 but setting
> KVM_BUS_LOCK_DETECTION_OFF is allowed. To keep it consistent, also
> return error for KVM_BUS_LOCK_DETECTION_OFF when KVM doesn't support
> BUS LOCK EXIT.
>
> Fixes: fe6b6bc802b4 ("KVM: VMX: Enable bus lock VM exit")
> Signed-off-by: Xiaoyao Li <xiaoyao.li@xxxxxxxxx>
> ---
> ---
> arch/x86/kvm/x86.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index d94b59140c45..3d8422d1cd04 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -4058,8 +4058,10 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
> (cap->args[0] & KVM_BUS_LOCK_DETECTION_EXIT))
> break;
>
> - if (kvm_caps.has_bus_lock_exit &&
> - cap->args[0] & KVM_BUS_LOCK_DETECTION_EXIT)
> + if (!kvm_caps.has_bus_lock_exit)
> + break;

Yikes, we really botched this one.

KVM unfortunately made KVM_BUS_LOCK_DETECTION_OFF an explicit flag, not an absense
of flags, without actually honoring that flag. E.g. doing KVM_BUS_LOCK_DETECTION_OFF
after KVM_BUS_LOCK_DETECTION_EXIT doesn't actually turn off detection.

I vote to get greedy and try dropping KVM_BUS_LOCK_DETECTION_OFF entirely, and
making it so that calling the CAP without any flags turns off detection. Otherwise
we have to either rejec that case (also risks breaking userspace) or treat it as
"do nothing" (which is just stupid). We'd want to reserve bit 0 to avoid really
bad breakage, i.e. so that we don't re-introduce bit 0 as something else, but
that's easy enough.

I'm thinking this over a few patches:

diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 1e64026d7c1e..b7c21675aa81 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -8379,7 +8379,6 @@ The valid mask flags are:

Valid bits in args[0] are::

- #define KVM_BUS_LOCK_DETECTION_OFF (1 << 0)
#define KVM_BUS_LOCK_DETECTION_EXIT (1 << 1)

Enabling this capability on a VM provides userspace with a way to select a
@@ -8393,8 +8392,8 @@ guest, irrespective whether or not the host has enabled split-lock detection
intended to mitigate attacks where a malicious/buggy guest can exploit bus
locks to degrade the performance of the whole system.

-If KVM_BUS_LOCK_DETECTION_OFF is set, KVM doesn't force guest bus locks to VM
-exit, although the host kernel's split-lock #AC detection still applies, if
+If KVM_BUS_LOCK_DETECTION_EXIT is not set, KVM doesn't force guest bus locks to
+VM exit, although the host kernel's split-lock #AC detection still applies, if
enabled.

If KVM_BUS_LOCK_DETECTION_EXIT is set, KVM enables a CPU feature that ensures
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 1ac60628b4c0..67791d139615 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -150,8 +150,8 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_host);
(KVM_X86_QUIRK_CD_NW_CLEARED | \
KVM_X86_QUIRK_IGNORE_GUEST_PAT)

-#define KVM_BUS_LOCK_DETECTION_VALID_MODE (KVM_BUS_LOCK_DETECTION_OFF | \
- KVM_BUS_LOCK_DETECTION_EXIT)
+/* Bit 0 is forever reserved to avoid breaking userspace in bad ways. */
+#define KVM_BUS_LOCK_DETECTION_VALID_MASK (KVM_BUS_LOCK_DETECTION_EXIT & ~BIT(0))

#define KVM_X86_NOTIFY_VMEXIT_VALID_BITS (KVM_X86_NOTIFY_VMEXIT_ENABLED | \
KVM_X86_NOTIFY_VMEXIT_USER)
@@ -2381,8 +2381,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
break;
case KVM_CAP_X86_BUS_LOCK_EXIT:
if (kvm_caps.has_bus_lock_exit)
- r = KVM_BUS_LOCK_DETECTION_OFF |
- KVM_BUS_LOCK_DETECTION_EXIT;
+ r = KVM_BUS_LOCK_DETECTION_VALID_MASK;
else
r = 0;
break;
@@ -4051,17 +4050,18 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
break;
case KVM_CAP_X86_BUS_LOCK_EXIT:
r = -EINVAL;
- if (cap->args[0] & ~KVM_BUS_LOCK_DETECTION_VALID_MODE)
+ if (!kvm_caps.has_bus_lock_exit)
break;

- if ((cap->args[0] & KVM_BUS_LOCK_DETECTION_OFF) &&
- (cap->args[0] & KVM_BUS_LOCK_DETECTION_EXIT))
+ if (cap->args[0] & ~KVM_BUS_LOCK_DETECTION_VALID_MASK)
break;

- if (kvm_caps.has_bus_lock_exit &&
- cap->args[0] & KVM_BUS_LOCK_DETECTION_EXIT)
- kvm->arch.bus_lock_detection_enabled = true;
- r = 0;
+ mutex_lock(&kvm->lock);
+ if (!kvm->created_vcpus) {
+ kvm->arch.bus_lock_detection_enabled = cap->args[0] & KVM_BUS_LOCK_DETECTION_EXIT;
+ r = 0;
+ }
+ mutex_unlock(&kvm->lock);
break;
#ifdef CONFIG_X86_SGX_KVM
case KVM_CAP_SGX_ATTRIBUTE: {
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 129d6f630325..08e5fe09e5c8 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1542,7 +1542,6 @@ struct kvm_dirty_gfn {
__u64 offset;
};

-#define KVM_BUS_LOCK_DETECTION_OFF (1 << 0)
#define KVM_BUS_LOCK_DETECTION_EXIT (1 << 1)

#define KVM_PMU_CAP_DISABLE (1 << 0)


> +
> + if (cap->args[0] & KVM_BUS_LOCK_DETECTION_EXIT)
> kvm->arch.bus_lock_detection_enabled = true;
> r = 0;
> break;
> --
> 2.43.0
>