From: Nicușor Cîțu <ncitu@xxxxxxxxxxxxxxx> This commit also ensures that the introspection tool and the userspace do not disable each other the descriptor-table access VM-exit. Signed-off-by: Nicușor Cîțu <ncitu@xxxxxxxxxxxxxxx> Signed-off-by: Adalbert Lazăr <alazar@xxxxxxxxxxxxxxx> --- arch/x86/include/asm/kvmi_host.h | 4 +++ arch/x86/kvm/kvmi.c | 45 ++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.c | 3 +++ arch/x86/kvm/vmx/vmx.c | 3 +++ 4 files changed, 55 insertions(+) diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index 09ebed80a8cc..0ed1879fd250 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -17,6 +17,7 @@ struct kvmi_interception { bool restore_interception; struct kvmi_monitor_interception breakpoint; struct kvmi_monitor_interception cr3w; + struct kvmi_monitor_interception descriptor; }; struct kvm_vcpu_arch_introspection { @@ -35,6 +36,7 @@ bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu); bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable); void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, u64 old_value, u64 new_value); +bool kvmi_monitor_desc_intercept(struct kvm_vcpu *vcpu, bool enable); bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write); #else /* CONFIG_KVM_INTROSPECTION */ @@ -50,6 +52,8 @@ static inline bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable) { return false; } static inline void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, u64 old_value, u64 new_value) { } +static inline bool kvmi_monitor_desc_intercept(struct kvm_vcpu *vcpu, + bool enable) { return false; } static inline bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write) { return true; } diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 3ae43a4c8764..dfe1b887b4f3 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -361,12 +361,52 @@ static void kvmi_arch_disable_cr3w_intercept(struct kvm_vcpu *vcpu) vcpu->arch.kvmi->cr3w.kvm_intercepted = false; } +/* + * Returns true if one side (kvm or kvmi) tries to disable the descriptor + * interception while the other side is still tracking it. + */ +bool kvmi_monitor_desc_intercept(struct kvm_vcpu *vcpu, bool enable) +{ + struct kvmi_interception *arch_vcpui = READ_ONCE(vcpu->arch.kvmi); + + return (arch_vcpui && arch_vcpui->descriptor.monitor_fct(vcpu, enable)); +} +EXPORT_SYMBOL(kvmi_monitor_desc_intercept); + +static bool monitor_desc_fct_kvmi(struct kvm_vcpu *vcpu, bool enable) +{ + vcpu->arch.kvmi->descriptor.kvmi_intercepted = enable; + + if (enable) + vcpu->arch.kvmi->descriptor.kvm_intercepted = + kvm_x86_ops.desc_intercepted(vcpu); + else if (vcpu->arch.kvmi->descriptor.kvm_intercepted) + return true; + + return false; +} + +static bool monitor_desc_fct_kvm(struct kvm_vcpu *vcpu, bool enable) +{ + if (!vcpu->arch.kvmi->descriptor.kvmi_intercepted) + return false; + + vcpu->arch.kvmi->descriptor.kvm_intercepted = enable; + + if (!enable) + return true; + + return false; +} + static int kvmi_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) { if (!kvm_x86_ops.desc_ctrl_supported()) return -KVM_EOPNOTSUPP; + vcpu->arch.kvmi->descriptor.monitor_fct = monitor_desc_fct_kvmi; kvm_x86_ops.control_desc_intercept(vcpu, enable); + vcpu->arch.kvmi->descriptor.monitor_fct = monitor_desc_fct_kvm; return 0; } @@ -374,6 +414,9 @@ static int kvmi_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) static void kvmi_arch_disable_desc_intercept(struct kvm_vcpu *vcpu) { kvmi_control_desc_intercept(vcpu, false); + + vcpu->arch.kvmi->descriptor.kvmi_intercepted = false; + vcpu->arch.kvmi->descriptor.kvm_intercepted = false; } int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu, @@ -445,11 +488,13 @@ bool kvmi_arch_vcpu_alloc_interception(struct kvm_vcpu *vcpu) arch_vcpui->breakpoint.monitor_fct = monitor_bp_fct_kvm; arch_vcpui->cr3w.monitor_fct = monitor_cr3w_fct_kvm; + arch_vcpui->descriptor.monitor_fct = monitor_desc_fct_kvm; /* * paired with: * - kvmi_monitor_bp_intercept() * - kvmi_monitor_cr3w_intercept() + * - kvmi_monitor_desc_intercept() */ smp_wmb(); WRITE_ONCE(vcpu->arch.kvmi, arch_vcpui); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index a0b91007e484..20f6905b45aa 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1555,6 +1555,9 @@ static void svm_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) { struct vcpu_svm *svm = to_svm(vcpu); + if (kvmi_monitor_desc_intercept(vcpu, enable)) + return; + if (enable) { set_intercept(svm, INTERCEPT_STORE_IDTR); set_intercept(svm, INTERCEPT_STORE_GDTR); diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 74bdcd4966ca..8d396a2d2309 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -3162,6 +3162,9 @@ static void vmx_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) { struct vcpu_vmx *vmx = to_vmx(vcpu); + if (kvmi_monitor_desc_intercept(vcpu, enable)) + return; + if (enable) secondary_exec_controls_setbit(vmx, SECONDARY_EXEC_DESC); else