diff mbox series

[v7,4/7] KVM: VMX: Load Guest CET via VMCS when CET is enabled in Guest

Message ID 20190927021927.23057-5-weijiang.yang@intel.com (mailing list archive)
State New, archived
Headers show
Series Introduce support for Guest CET feature | expand

Commit Message

Yang, Weijiang Sept. 27, 2019, 2:19 a.m. UTC
"Load Guest CET state" bit controls whether Guest CET states
will be loaded at Guest entry. Before doing that, KVM needs
to check if CPU CET feature is enabled on host and available
to Guest.

Note: SHSTK and IBT features share one control MSR:
MSR_IA32_{U,S}_CET, which means it's difficult to hide
one feature from another in the case of SHSTK != IBT,
after discussed in community, it's agreed to allow Guest
control two features independently as it won't introduce
security hole.

Co-developed-by: Zhang Yi Z <yi.z.zhang@linux.intel.com>
Signed-off-by: Zhang Yi Z <yi.z.zhang@linux.intel.com>
Signed-off-by: Yang Weijiang <weijiang.yang@intel.com>
---
 arch/x86/kvm/vmx/vmx.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

Comments

Jim Mattson Oct. 2, 2019, 6:54 p.m. UTC | #1
On Thu, Sep 26, 2019 at 7:17 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
>
> "Load Guest CET state" bit controls whether Guest CET states
> will be loaded at Guest entry. Before doing that, KVM needs
> to check if CPU CET feature is enabled on host and available
> to Guest.
>
> Note: SHSTK and IBT features share one control MSR:
> MSR_IA32_{U,S}_CET, which means it's difficult to hide
> one feature from another in the case of SHSTK != IBT,
> after discussed in community, it's agreed to allow Guest
> control two features independently as it won't introduce
> security hole.
>
> Co-developed-by: Zhang Yi Z <yi.z.zhang@linux.intel.com>
> Signed-off-by: Zhang Yi Z <yi.z.zhang@linux.intel.com>
> Signed-off-by: Yang Weijiang <weijiang.yang@intel.com>
> ---
>  arch/x86/kvm/vmx/vmx.c | 35 +++++++++++++++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
>
> diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
> index f720baa7a9ba..ba1a83d11e69 100644
> --- a/arch/x86/kvm/vmx/vmx.c
> +++ b/arch/x86/kvm/vmx/vmx.c
> @@ -44,6 +44,7 @@
>  #include <asm/spec-ctrl.h>
>  #include <asm/virtext.h>
>  #include <asm/vmx.h>
> +#include <asm/cet.h>
>
>  #include "capabilities.h"
>  #include "cpuid.h"
> @@ -2918,6 +2919,37 @@ void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
>         vmcs_writel(GUEST_CR3, guest_cr3);
>  }
>
> +static int set_cet_bit(struct kvm_vcpu *vcpu, unsigned long cr4)

Nit: This function does not appear to set CR4.CET, as the name would imply.

> +{
> +       struct vcpu_vmx *vmx = to_vmx(vcpu);
> +       const u64 cet_bits = XFEATURE_MASK_CET_USER | XFEATURE_MASK_CET_KERNEL;
> +       bool cet_xss = vmx_xsaves_supported() &&
> +                      (kvm_supported_xss() & cet_bits);
> +       bool cet_cpuid = guest_cpuid_has(vcpu, X86_FEATURE_SHSTK) ||
> +                        guest_cpuid_has(vcpu, X86_FEATURE_IBT);
> +       bool cet_on = !!(cr4 & X86_CR4_CET);
> +
> +       if (cet_on && vmx->nested.vmxon)
> +               return 1;

This constraint doesn't appear to be architected. Also, this prevents
enabling CR4.CET when in VMX operation, but what about the other way
around (i.e. entering VMX operation with CR4.CET enabled)?

> +       if (cet_on && !cpu_x86_cet_enabled())
> +               return 1;

This seems odd. Why is kernel support for (SHSTK or IBT) required for
the guest to use (SHSTK or IBT)? If there's a constraint, shouldn't it
be 1:1? (i.e. kernel support for SHSTK is required for the guest to
use SHSTK and kernel support for IBT is required for the guest to use
IBT?) Either way, enforcement of this constraint seems late, here,
when the guest is trying to set CR4 to a value that, per the guest
CPUID information, should be legal. Shouldn't this constraint be
applied when setting the guest CPUID information, disallowing the
enumeration of SHSTK and/or IBT support on a platform where these
features are unavailable or disabled in the kernel?

> +       if (cet_on && !cet_xss)
> +               return 1;

Again, this constraint seems like it's being applied too late.
Returning 1 here will result in the guest's MOV-to-CR4 raising #GP,
even though there is no architected reason for it to do so.

> +       if (cet_on && !cet_cpuid)
> +               return 1;

What about the constraint that CR4.CET can't be set when CR0.WP is
clear? (And the reverse needs to be handled in vmx_set_cr0).

> +       if (cet_on)
> +               vmcs_set_bits(VM_ENTRY_CONTROLS,
> +                             VM_ENTRY_LOAD_GUEST_CET_STATE);

Have we ensured that this VM-entry control is supported on the platform?

> +       else
> +               vmcs_clear_bits(VM_ENTRY_CONTROLS,
> +                               VM_ENTRY_LOAD_GUEST_CET_STATE);
> +       return 0;
> +}
> +
>  int vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
>  {
>         struct vcpu_vmx *vmx = to_vmx(vcpu);
> @@ -2958,6 +2990,9 @@ int vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
>                         return 1;
>         }
>
> +       if (set_cet_bit(vcpu, cr4))
> +               return 1;
> +
>         if (vmx->nested.vmxon && !nested_cr4_valid(vcpu, cr4))
>                 return 1;
>
> --
> 2.17.2
>
Yang, Weijiang Oct. 9, 2019, 6:43 a.m. UTC | #2
On Wed, Oct 02, 2019 at 11:54:26AM -0700, Jim Mattson wrote:
> On Thu, Sep 26, 2019 at 7:17 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
> >
> > "Load Guest CET state" bit controls whether Guest CET states
> > will be loaded at Guest entry. Before doing that, KVM needs
> > to check if CPU CET feature is enabled on host and available
> > to Guest.
> >
> > Note: SHSTK and IBT features share one control MSR:
> > MSR_IA32_{U,S}_CET, which means it's difficult to hide
> > one feature from another in the case of SHSTK != IBT,
> > after discussed in community, it's agreed to allow Guest
> > control two features independently as it won't introduce
> > security hole.
> >
> > Co-developed-by: Zhang Yi Z <yi.z.zhang@linux.intel.com>
> > Signed-off-by: Zhang Yi Z <yi.z.zhang@linux.intel.com>
> > Signed-off-by: Yang Weijiang <weijiang.yang@intel.com>
> > ---
> >  arch/x86/kvm/vmx/vmx.c | 35 +++++++++++++++++++++++++++++++++++
> >  1 file changed, 35 insertions(+)
> >
> > diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
> > index f720baa7a9ba..ba1a83d11e69 100644
> > --- a/arch/x86/kvm/vmx/vmx.c
> > +++ b/arch/x86/kvm/vmx/vmx.c
> > @@ -44,6 +44,7 @@
> >  #include <asm/spec-ctrl.h>
> >  #include <asm/virtext.h>
> >  #include <asm/vmx.h>
> > +#include <asm/cet.h>
> >
> >  #include "capabilities.h"
> >  #include "cpuid.h"
> > @@ -2918,6 +2919,37 @@ void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
> >         vmcs_writel(GUEST_CR3, guest_cr3);
> >  }
> >
> > +static int set_cet_bit(struct kvm_vcpu *vcpu, unsigned long cr4)
> 
> Nit: This function does not appear to set CR4.CET, as the name would imply.
>
OK, will change it, thank you!
> > +{
> > +       struct vcpu_vmx *vmx = to_vmx(vcpu);
> > +       const u64 cet_bits = XFEATURE_MASK_CET_USER | XFEATURE_MASK_CET_KERNEL;
> > +       bool cet_xss = vmx_xsaves_supported() &&
> > +                      (kvm_supported_xss() & cet_bits);
> > +       bool cet_cpuid = guest_cpuid_has(vcpu, X86_FEATURE_SHSTK) ||
> > +                        guest_cpuid_has(vcpu, X86_FEATURE_IBT);
> > +       bool cet_on = !!(cr4 & X86_CR4_CET);
> > +
> > +       if (cet_on && vmx->nested.vmxon)
> > +               return 1;
> 
> This constraint doesn't appear to be architected. Also, this prevents
> enabling CR4.CET when in VMX operation, but what about the other way
> around (i.e. entering VMX operation with CR4.CET enabled)?
> 
Yes, will think more for nested case in next release.

> > +       if (cet_on && !cpu_x86_cet_enabled())
> > +               return 1;
> 
> This seems odd. Why is kernel support for (SHSTK or IBT) required for
> the guest to use (SHSTK or IBT)? If there's a constraint, shouldn't it
> be 1:1? (i.e. kernel support for SHSTK is required for the guest to
> use SHSTK and kernel support for IBT is required for the guest to use
> IBT?) Either way, enforcement of this constraint seems late, here,
> when the guest is trying to set CR4 to a value that, per the guest
> CPUID information, should be legal. Shouldn't this constraint be
> applied when setting the guest CPUID information, disallowing the
> enumeration of SHSTK and/or IBT support on a platform where these
> features are unavailable or disabled in the kernel?
> 
In KVM do_cpuid_7_mask(), SHSTK/IBT flags are emulated with
cpuid(0x7,0), there in cpuid_mask(), it'll enforce the check against
host kernel CET status,
and host boot_cpu_data.x86_capability[X86_FEATURE_SHSTK/IBT] will be cleared
during host boot up if the feature is disabled or unavailable.

You may check the kernel CET patch.

Rgarding the 1:1 check, I'll add more strict check in next version,
thanks!

> > +       if (cet_on && !cet_xss)
> > +               return 1;
> 
> Again, this constraint seems like it's being applied too late.
> Returning 1 here will result in the guest's MOV-to-CR4 raising #GP,
> even though there is no architected reason for it to do so.
> 
see above.
> > +       if (cet_on && !cet_cpuid)
> > +               return 1;
> 
> What about the constraint that CR4.CET can't be set when CR0.WP is
> clear? (And the reverse needs to be handled in vmx_set_cr0).
> 
OK, will check this case, thank you!
> > +       if (cet_on)
> > +               vmcs_set_bits(VM_ENTRY_CONTROLS,
> > +                             VM_ENTRY_LOAD_GUEST_CET_STATE);
> 
> Have we ensured that this VM-entry control is supported on the platform?
> 
If all the checks pass, is it enought to ensure the control bit supported?
> > +       else
> > +               vmcs_clear_bits(VM_ENTRY_CONTROLS,
> > +                               VM_ENTRY_LOAD_GUEST_CET_STATE);
> > +       return 0;
> > +}
> > +
> >  int vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
> >  {
> >         struct vcpu_vmx *vmx = to_vmx(vcpu);
> > @@ -2958,6 +2990,9 @@ int vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
> >                         return 1;
> >         }
> >
> > +       if (set_cet_bit(vcpu, cr4))
> > +               return 1;
> > +
> >         if (vmx->nested.vmxon && !nested_cr4_valid(vcpu, cr4))
> >                 return 1;
> >
> > --
> > 2.17.2
> >
Jim Mattson Oct. 9, 2019, 11:08 p.m. UTC | #3
On Tue, Oct 8, 2019 at 11:41 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
>
> On Wed, Oct 02, 2019 at 11:54:26AM -0700, Jim Mattson wrote:
> > On Thu, Sep 26, 2019 at 7:17 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
> > > +       if (cet_on)
> > > +               vmcs_set_bits(VM_ENTRY_CONTROLS,
> > > +                             VM_ENTRY_LOAD_GUEST_CET_STATE);
> >
> > Have we ensured that this VM-entry control is supported on the platform?
> >
> If all the checks pass, is it enought to ensure the control bit supported?

I don't think so. The only way to check to see if a VM-entry control
is supported is to check the relevant VMX capability MSR.

BTW, what about the corresponding VM-exit control?
Yang, Weijiang Oct. 10, 2019, 1:30 a.m. UTC | #4
On Wed, Oct 09, 2019 at 04:08:50PM -0700, Jim Mattson wrote:
> On Tue, Oct 8, 2019 at 11:41 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
> >
> > On Wed, Oct 02, 2019 at 11:54:26AM -0700, Jim Mattson wrote:
> > > On Thu, Sep 26, 2019 at 7:17 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
> > > > +       if (cet_on)
> > > > +               vmcs_set_bits(VM_ENTRY_CONTROLS,
> > > > +                             VM_ENTRY_LOAD_GUEST_CET_STATE);
> > >
> > > Have we ensured that this VM-entry control is supported on the platform?
> > >
> > If all the checks pass, is it enought to ensure the control bit supported?
> 
> I don't think so. The only way to check to see if a VM-entry control
> is supported is to check the relevant VMX capability MSR.
>
It's a bit odd, there's no relevant CET bit in VMX cap. MSR, so I have
to check like this.

> BTW, what about the corresponding VM-exit control?
The kernel supervisor mode CET is not implemented yet, so I don't load host CET
states on VM-exit, in future, I'll add it.
Jim Mattson Oct. 10, 2019, 11:44 p.m. UTC | #5
On Wed, Oct 9, 2019 at 6:28 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
>
> On Wed, Oct 09, 2019 at 04:08:50PM -0700, Jim Mattson wrote:
> > On Tue, Oct 8, 2019 at 11:41 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
> > >
> > > On Wed, Oct 02, 2019 at 11:54:26AM -0700, Jim Mattson wrote:
> > > > On Thu, Sep 26, 2019 at 7:17 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
> > > > > +       if (cet_on)
> > > > > +               vmcs_set_bits(VM_ENTRY_CONTROLS,
> > > > > +                             VM_ENTRY_LOAD_GUEST_CET_STATE);
> > > >
> > > > Have we ensured that this VM-entry control is supported on the platform?
> > > >
> > > If all the checks pass, is it enought to ensure the control bit supported?
> >
> > I don't think so. The only way to check to see if a VM-entry control
> > is supported is to check the relevant VMX capability MSR.
> >
> It's a bit odd, there's no relevant CET bit in VMX cap. MSR, so I have
> to check like this.

Bit 52 of the IA32_VMX_ENTRY_CTLS MSR (index 484H) [and bit 52 of the
IA32_VMX_TRUE_ENTRY_CTLS MSR (index 490H), on hardware that supports
the "true" VMX capability MSRs] will be 1 if it is legal to set bit 20
of the VM-entry controls field to 1.

> > BTW, what about the corresponding VM-exit control?
> The kernel supervisor mode CET is not implemented yet, so I don't load host CET
> states on VM-exit, in future, I'll add it.

If you don't clear the supervisor mode CET state on VM-exit and the
guest has set IA32_S_CET.SH_STK_EN, doesn't that mean that
supervisor-mode shadow stacks will then be enabled on the host after
VM-exit?
Yang, Weijiang Oct. 11, 2019, 1:43 a.m. UTC | #6
On Thu, Oct 10, 2019 at 04:44:17PM -0700, Jim Mattson wrote:
> On Wed, Oct 9, 2019 at 6:28 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
> >
> > On Wed, Oct 09, 2019 at 04:08:50PM -0700, Jim Mattson wrote:
> > > On Tue, Oct 8, 2019 at 11:41 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
> > > >
> > > > On Wed, Oct 02, 2019 at 11:54:26AM -0700, Jim Mattson wrote:
> > > > > On Thu, Sep 26, 2019 at 7:17 PM Yang Weijiang <weijiang.yang@intel.com> wrote:
> > > > > > +       if (cet_on)
> > > > > > +               vmcs_set_bits(VM_ENTRY_CONTROLS,
> > > > > > +                             VM_ENTRY_LOAD_GUEST_CET_STATE);
> > > > >
> > > > > Have we ensured that this VM-entry control is supported on the platform?
> > > > >
> > > > If all the checks pass, is it enought to ensure the control bit supported?
> > >
> > > I don't think so. The only way to check to see if a VM-entry control
> > > is supported is to check the relevant VMX capability MSR.
> > >
> > It's a bit odd, there's no relevant CET bit in VMX cap. MSR, so I have
> > to check like this.
> 
> Bit 52 of the IA32_VMX_ENTRY_CTLS MSR (index 484H) [and bit 52 of the
> IA32_VMX_TRUE_ENTRY_CTLS MSR (index 490H), on hardware that supports
> the "true" VMX capability MSRs] will be 1 if it is legal to set bit 20
> of the VM-entry controls field to 1.
>
Oh, you meant this, I'll add the check, thanks.

> > > BTW, what about the corresponding VM-exit control?
> > The kernel supervisor mode CET is not implemented yet, so I don't load host CET
> > states on VM-exit, in future, I'll add it.
> 
> If you don't clear the supervisor mode CET state on VM-exit and the
> guest has set IA32_S_CET.SH_STK_EN, doesn't that mean that
> supervisor-mode shadow stacks will then be enabled on the host after
> VM-exit?

Yeah, I should clear the MSR on VM-exit before supervisor-mode is
implemented. Thank you!

BTW, I'll move this bit-set before VM-entry, vmx_set_cr4() is not a
suitable place to set the bit.
diff mbox series

Patch

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index f720baa7a9ba..ba1a83d11e69 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -44,6 +44,7 @@ 
 #include <asm/spec-ctrl.h>
 #include <asm/virtext.h>
 #include <asm/vmx.h>
+#include <asm/cet.h>
 
 #include "capabilities.h"
 #include "cpuid.h"
@@ -2918,6 +2919,37 @@  void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
 	vmcs_writel(GUEST_CR3, guest_cr3);
 }
 
+static int set_cet_bit(struct kvm_vcpu *vcpu, unsigned long cr4)
+{
+	struct vcpu_vmx *vmx = to_vmx(vcpu);
+	const u64 cet_bits = XFEATURE_MASK_CET_USER | XFEATURE_MASK_CET_KERNEL;
+	bool cet_xss = vmx_xsaves_supported() &&
+		       (kvm_supported_xss() & cet_bits);
+	bool cet_cpuid = guest_cpuid_has(vcpu, X86_FEATURE_SHSTK) ||
+			 guest_cpuid_has(vcpu, X86_FEATURE_IBT);
+	bool cet_on = !!(cr4 & X86_CR4_CET);
+
+	if (cet_on && vmx->nested.vmxon)
+		return 1;
+
+	if (cet_on && !cpu_x86_cet_enabled())
+		return 1;
+
+	if (cet_on && !cet_xss)
+		return 1;
+
+	if (cet_on && !cet_cpuid)
+		return 1;
+
+	if (cet_on)
+		vmcs_set_bits(VM_ENTRY_CONTROLS,
+			      VM_ENTRY_LOAD_GUEST_CET_STATE);
+	else
+		vmcs_clear_bits(VM_ENTRY_CONTROLS,
+				VM_ENTRY_LOAD_GUEST_CET_STATE);
+	return 0;
+}
+
 int vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
 {
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
@@ -2958,6 +2990,9 @@  int vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
 			return 1;
 	}
 
+	if (set_cet_bit(vcpu, cr4))
+		return 1;
+
 	if (vmx->nested.vmxon && !nested_cr4_valid(vcpu, cr4))
 		return 1;