diff mbox series

[v3,02/17] KVM: x86/xen: fix Xen hypercall page msr handling

Message ID 20201214083905.2017260-3-dwmw2@infradead.org (mailing list archive)
State New, archived
Headers show
Series KVM: Add minimal support for Xen HVM guests | expand

Commit Message

David Woodhouse Dec. 14, 2020, 8:38 a.m. UTC
From: Joao Martins <joao.m.martins@oracle.com>

Xen usually places its MSR at 0x40000000 or 0x40000200 depending on
whether it is running in viridian mode or not. Note that this is not
ABI guaranteed, so it is possible for Xen to advertise the MSR some
place else.

Given the way xen_hvm_config() is handled, if the former address is
selected, this will conflict with Hyper-V's MSR
(HV_X64_MSR_GUEST_OS_ID) which unconditionally uses the same address.

Given that the MSR location is arbitrary, move the xen_hvm_config()
handling to the top of kvm_set_msr_common() before falling through.

Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
 arch/x86/kvm/x86.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

Comments

Vitaly Kuznetsov Dec. 14, 2020, 9:27 p.m. UTC | #1
David Woodhouse <dwmw2@infradead.org> writes:

> From: Joao Martins <joao.m.martins@oracle.com>
>
> Xen usually places its MSR at 0x40000000 or 0x40000200 depending on
> whether it is running in viridian mode or not. Note that this is not
> ABI guaranteed, so it is possible for Xen to advertise the MSR some
> place else.
>
> Given the way xen_hvm_config() is handled, if the former address is
> selected, this will conflict with Hyper-V's MSR
> (HV_X64_MSR_GUEST_OS_ID) which unconditionally uses the same address.
>
> Given that the MSR location is arbitrary, move the xen_hvm_config()
> handling to the top of kvm_set_msr_common() before falling through.
>

In case we're making MSR 0x40000000 something different from
HV_X64_MSR_GUEST_OS_ID we can and probably should disable Hyper-V
emulation in KVM completely -- or how else is it going to work?

> Signed-off-by: Joao Martins <joao.m.martins@oracle.com>
> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
> ---
>  arch/x86/kvm/x86.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index c7f1ba21212e..13ba4a64f748 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -3001,6 +3001,9 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
>  	u32 msr = msr_info->index;
>  	u64 data = msr_info->data;
>  
> +	if (msr && (msr == vcpu->kvm->arch.xen_hvm_config.msr))
> +		return xen_hvm_config(vcpu, data);
> +

Can we generalize this maybe? E.g. before handling KVM and architectural
MSRs we check that the particular MSR is not overriden by an emulated
hypervisor, 

e.g.
	if (kvm_emulating_hyperv(kvm) && kvm_hyperv_msr_overriden(kvm,msr)
		return kvm_hyperv_handle_msr(kvm, msr);
	if (kvm_emulating_xen(kvm) && kvm_xen_msr_overriden(kvm,msr)
		return kvm_xen_handle_msr(kvm, msr);

	switch (msr) {
		...

>  	switch (msr) {
>  	case MSR_AMD64_NB_CFG:
>  	case MSR_IA32_UCODE_WRITE:
> @@ -3288,8 +3291,6 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
>  		vcpu->arch.msr_misc_features_enables = data;
>  		break;
>  	default:
> -		if (msr && (msr == vcpu->kvm->arch.xen_hvm_config.msr))
> -			return xen_hvm_config(vcpu, data);
>  		if (kvm_pmu_is_valid_msr(vcpu, msr))
>  			return kvm_pmu_set_msr(vcpu, msr_info);
>  		return KVM_MSR_RET_INVALID;
David Woodhouse Dec. 14, 2020, 9:35 p.m. UTC | #2
On 14 December 2020 21:27:19 GMT, Vitaly Kuznetsov <vkuznets@redhat.com> wrote:
>David Woodhouse <dwmw2@infradead.org> writes:
>
>> From: Joao Martins <joao.m.martins@oracle.com>
>>
>> Xen usually places its MSR at 0x40000000 or 0x40000200 depending on
>> whether it is running in viridian mode or not. Note that this is not
>> ABI guaranteed, so it is possible for Xen to advertise the MSR some
>> place else.
>>
>> Given the way xen_hvm_config() is handled, if the former address is
>> selected, this will conflict with Hyper-V's MSR
>> (HV_X64_MSR_GUEST_OS_ID) which unconditionally uses the same address.
>>
>> Given that the MSR location is arbitrary, move the xen_hvm_config()
>> handling to the top of kvm_set_msr_common() before falling through.
>>
>
>In case we're making MSR 0x40000000 something different from
>HV_X64_MSR_GUEST_OS_ID we can and probably should disable Hyper-V
>emulation in KVM completely -- or how else is it going to work?
 
The way Xen itself does this — and the way we have to do it if we want to faithfully emulate Xen and support live migration from it — is to shift the Xen MSRs up to (from memory) 0x40000200 if Hyper-V is enabled.

I did look at disabling Hyper-V entirely when it isn't enabled, but the only flag we have for it being enabled is the guest OS ID being set... which is done through that MSR :)

My minimal version ended up being so close to Joao's original that it was not longer worth the bikeshedding and so I gave up on it and stuck with the original.


>> @@ -3001,6 +3001,9 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu,
>struct msr_data *msr_info)
>>  	u32 msr = msr_info->index;
>>  	u64 data = msr_info->data;
>>  
>> +	if (msr && (msr == vcpu->kvm->arch.xen_hvm_config.msr))
>> +		return xen_hvm_config(vcpu, data);
>> +
>
>Can we generalize this maybe? E.g. before handling KVM and
>architectural
>MSRs we check that the particular MSR is not overriden by an emulated
>hypervisor, 
>
>e.g.
>	if (kvm_emulating_hyperv(kvm) && kvm_hyperv_msr_overriden(kvm,msr)
>		return kvm_hyperv_handle_msr(kvm, msr);
>	if (kvm_emulating_xen(kvm) && kvm_xen_msr_overriden(kvm,msr)
>		return kvm_xen_handle_msr(kvm, msr);

That smells a bit like overengineering. As I said, I did have a play with "improving" Joao's original patch but nothing I tried actually made more sense to me than this once the details were ironed out.
Vitaly Kuznetsov Dec. 14, 2020, 9:44 p.m. UTC | #3
David Woodhouse <dwmw2@infradead.org> writes:

>>> @@ -3001,6 +3001,9 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu,
>>struct msr_data *msr_info)
>>>  	u32 msr = msr_info->index;
>>>  	u64 data = msr_info->data;
>>>  
>>> +	if (msr && (msr == vcpu->kvm->arch.xen_hvm_config.msr))
>>> +		return xen_hvm_config(vcpu, data);
>>> +
>>
>>Can we generalize this maybe? E.g. before handling KVM and
>>architectural
>>MSRs we check that the particular MSR is not overriden by an emulated
>>hypervisor, 
>>
>>e.g.
>>	if (kvm_emulating_hyperv(kvm) && kvm_hyperv_msr_overriden(kvm,msr)
>>		return kvm_hyperv_handle_msr(kvm, msr);
>>	if (kvm_emulating_xen(kvm) && kvm_xen_msr_overriden(kvm,msr)
>>		return kvm_xen_handle_msr(kvm, msr);
>
> That smells a bit like overengineering. As I said, I did have a play
> with "improving" Joao's original patch but nothing I tried actually
> made more sense to me than this once the details were ironed out.

This actually looks more or less like hypercall distinction from after PATCH3:

	if (kvm_xen_hypercall_enabled(vcpu->kvm))
		return kvm_xen_hypercall(vcpu);

        if (kvm_hv_hypercall_enabled(vcpu->kvm))
  	        return kvm_hv_hypercall(vcpu);

....

so my idea was why not do the same for MSRs?
David Woodhouse Dec. 14, 2020, 9:48 p.m. UTC | #4
On 14 December 2020 21:44:47 GMT, Vitaly Kuznetsov <vkuznets@redhat.com> wrote:
>This actually looks more or less like hypercall distinction from after
>PATCH3:
>
>	if (kvm_xen_hypercall_enabled(vcpu->kvm))
>		return kvm_xen_hypercall(vcpu);
>
>        if (kvm_hv_hypercall_enabled(vcpu->kvm))
>  	        return kvm_hv_hypercall(vcpu);
>
>....
>
>so my idea was why not do the same for MSRs?

Can you define kvm_hv_msr_enabled()?

Note kvm_hv_hypercall_enabled() is based on a value that gets written through the MSR, so it can't be that.
Vitaly Kuznetsov Dec. 14, 2020, 10:22 p.m. UTC | #5
David Woodhouse <dwmw2@infradead.org> writes:

> On 14 December 2020 21:44:47 GMT, Vitaly Kuznetsov <vkuznets@redhat.com> wrote:
>>This actually looks more or less like hypercall distinction from after
>>PATCH3:
>>
>>	if (kvm_xen_hypercall_enabled(vcpu->kvm))
>>		return kvm_xen_hypercall(vcpu);
>>
>>        if (kvm_hv_hypercall_enabled(vcpu->kvm))
>>  	        return kvm_hv_hypercall(vcpu);
>>
>>....
>>
>>so my idea was why not do the same for MSRs?
>
> Can you define kvm_hv_msr_enabled()?
>
> Note kvm_hv_hypercall_enabled() is based on a value that gets written
> through the MSR, so it can't be that.

When Hyper-V emulation appeared in KVM we (unfortunately) didn't add a
capability to globaly enable and disable it so to be backwards
compatible we'll have to define kvm_emulating_hyperv() as 'true' for
now as that's how KVM behaves. This, however, doesn't mean we can't add
e.g. a module parameter to disable Hyper-V emulation. Also, we can
probably check guest CPUIDs and if Hyper-V's signature wasn't set we can
return 'false'.

<rant>
Having Hyper-V emulation in KVM 'always enabled' may not be a big deal
from functional point of view but may not be ideal from security
standpoint as bugs in arch/x86/kvm/hyperv.c become exploitable even from
Linux guests.
</rant>
David Woodhouse Dec. 14, 2020, 10:41 p.m. UTC | #6
On Mon, 2020-12-14 at 23:22 +0100, Vitaly Kuznetsov wrote:
> > Can you define kvm_hv_msr_enabled()?
> > 
> > Note kvm_hv_hypercall_enabled() is based on a value that gets written
> > through the MSR, so it can't be that.
> 
> When Hyper-V emulation appeared in KVM we (unfortunately) didn't add a
> capability to globaly enable and disable it so to be backwards
> compatible we'll have to define kvm_emulating_hyperv() as 'true' for
> now as that's how KVM behaves. This, however, doesn't mean we can't add
> e.g. a module parameter to disable Hyper-V emulation. Also, we can
> probably check guest CPUIDs and if Hyper-V's signature wasn't set we can
> return 'false'.
> 
> <rant>
> Having Hyper-V emulation in KVM 'always enabled' may not be a big deal
> from functional point of view but may not be ideal from security
> standpoint as bugs in arch/x86/kvm/hyperv.c become exploitable even from
> Linux guests.
> </rant>

Indeed. And yet it can coexist with Xen support too, so it isn't even
as simple as turning it off when Xen is enabled.

Which is why I ended up just using Joao's patch unchanged. Short of
going back in time to make Hyper-V support conditional when it was
first introduced, I couldn't see a better answer.

And regardless of the Hyper-V mess, what this patch does for Xen is
precisely what you suggest: handle it first, before the switch(), *if*
the Xen MSR is enabled.
Vitaly Kuznetsov Dec. 15, 2020, 12:10 p.m. UTC | #7
David Woodhouse <dwmw2@infradead.org> writes:

> On Mon, 2020-12-14 at 23:22 +0100, Vitaly Kuznetsov wrote:
>> > Can you define kvm_hv_msr_enabled()?
>> > 
>> > Note kvm_hv_hypercall_enabled() is based on a value that gets written
>> > through the MSR, so it can't be that.
>> 
>> When Hyper-V emulation appeared in KVM we (unfortunately) didn't add a
>> capability to globaly enable and disable it so to be backwards
>> compatible we'll have to define kvm_emulating_hyperv() as 'true' for
>> now as that's how KVM behaves. This, however, doesn't mean we can't add
>> e.g. a module parameter to disable Hyper-V emulation. Also, we can
>> probably check guest CPUIDs and if Hyper-V's signature wasn't set we can
>> return 'false'.
>> 
>> <rant>
>> Having Hyper-V emulation in KVM 'always enabled' may not be a big deal
>> from functional point of view but may not be ideal from security
>> standpoint as bugs in arch/x86/kvm/hyperv.c become exploitable even from
>> Linux guests.
>> </rant>
>
> Indeed. And yet it can coexist with Xen support too, so it isn't even
> as simple as turning it off when Xen is enabled.
>
> Which is why I ended up just using Joao's patch unchanged. Short of
> going back in time to make Hyper-V support conditional when it was
> first introduced, I couldn't see a better answer.
>
> And regardless of the Hyper-V mess, what this patch does for Xen is
> precisely what you suggest: handle it first, before the switch(), *if*
> the Xen MSR is enabled.

Functionally I have no complaints, even with the suggested
'generalization' we'll be handling MSRs in the exact same sequence. You
are, however, right calling Hyper-V mess 'mess' and if we want to make
things cleaner we should probably start there (goes to my to-do
list...).
Christoph Hellwig Dec. 23, 2020, 8:35 a.m. UTC | #8
> +	if (msr && (msr == vcpu->kvm->arch.xen_hvm_config.msr))
> +		return xen_hvm_config(vcpu, data);

Nit: no need for the inner braces.
diff mbox series

Patch

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index c7f1ba21212e..13ba4a64f748 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3001,6 +3001,9 @@  int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 	u32 msr = msr_info->index;
 	u64 data = msr_info->data;
 
+	if (msr && (msr == vcpu->kvm->arch.xen_hvm_config.msr))
+		return xen_hvm_config(vcpu, data);
+
 	switch (msr) {
 	case MSR_AMD64_NB_CFG:
 	case MSR_IA32_UCODE_WRITE:
@@ -3288,8 +3291,6 @@  int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 		vcpu->arch.msr_misc_features_enables = data;
 		break;
 	default:
-		if (msr && (msr == vcpu->kvm->arch.xen_hvm_config.msr))
-			return xen_hvm_config(vcpu, data);
 		if (kvm_pmu_is_valid_msr(vcpu, msr))
 			return kvm_pmu_set_msr(vcpu, msr_info);
 		return KVM_MSR_RET_INVALID;