Re: [PATCH 09/10] KVM: nVMX: include shadow vmcs12 in nested state

From: Jim Mattson
Date: Mon Jul 30 2018 - 15:11:56 EST


On Sat, Jul 28, 2018 at 4:10 PM, Paolo Bonzini <pbonzini@xxxxxxxxxx> wrote:
> The shadow vmcs12 cannot be flushed on KVM_GET_NESTED_STATE,
> because at that point guest memory is assumed by userspace to
> be immutable. Capture the cache in vmx_get_nested_state, adding
> another page at the end if there is an active shadow vmcs12.
>
> Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
> ---
> arch/x86/kvm/vmx.c | 31 ++++++++++++++++++++++++++++++-
> 1 file changed, 30 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
> index b5ee9e08bb48..ce8c0c759a19 100644
> --- a/arch/x86/kvm/vmx.c
> +++ b/arch/x86/kvm/vmx.c
> @@ -13167,9 +13167,15 @@ static int vmx_get_nested_state(struct kvm_vcpu *vcpu,
> kvm_state.vmx.vmxon_pa = vmx->nested.vmxon_ptr;
> kvm_state.vmx.vmcs_pa = vmx->nested.current_vmptr;
>
> - if (vmx->nested.current_vmptr != -1ull)
> + if (vmx->nested.current_vmptr != -1ull) {
> kvm_state.size += VMCS12_SIZE;
>
> + if (is_guest_mode(vcpu) &&
> + nested_cpu_has_shadow_vmcs(vmcs12) &&
> + vmcs12->vmcs_link_pointer != -1ull)
> + kvm_state.size += VMCS12_SIZE;
> + }
> +
> if (vmx->nested.smm.vmxon)
> kvm_state.vmx.smm.flags |= KVM_STATE_NESTED_SMM_VMXON;
>
> @@ -13208,6 +13214,13 @@ static int vmx_get_nested_state(struct kvm_vcpu *vcpu,
> if (copy_to_user(user_kvm_nested_state->data, vmcs12, sizeof(*vmcs12)))
> return -EFAULT;
>
> + if (nested_cpu_has_shadow_vmcs(vmcs12) &&
> + vmcs12->vmcs_link_pointer != -1ull) {
> + if (copy_to_user(user_kvm_nested_state->data + VMCS12_SIZE,
> + get_shadow_vmcs12(vcpu), sizeof(*vmcs12)))

Does this work with CONFIG_HARDENED_USERCOPY?

Is VMCS12_SIZE better than sizeof(*vmcs12)? What if we are migrating
to a destination where sizeof(*vmcs12) is larger than it is on the
source? If userspace doesn't zero the buffer before calling the ioctl,
then the destination may interpret nonsense as actual VMCS field
values. However, if we copy the entire page from the kernel, then we
know that anything beyond the source's sizeof(*vmcs12) will be zero.

> + return -EFAULT;
> + }
> +
> out:
> return kvm_state.size;
> }
> @@ -13288,6 +13301,22 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu,
> vmx->nested.nested_run_pending =
> !!(kvm_state->flags & KVM_STATE_NESTED_RUN_PENDING);
>
> + if (nested_cpu_has_shadow_vmcs(vmcs12) &&
> + vmcs12->vmcs_link_pointer != -1ull) {
> + struct vmcs12 *shadow_vmcs12 = get_shadow_vmcs12(vcpu);
> + if (kvm_state->size < sizeof(kvm_state) + 2 * sizeof(*vmcs12))
> + return -EINVAL;
> +
> + if (copy_from_user(shadow_vmcs12,
> + user_kvm_nested_state->data + VMCS12_SIZE,
> + sizeof(*vmcs12)))
> + return -EFAULT;
> +
> + if (shadow_vmcs12->hdr.revision_id != VMCS12_REVISION ||
> + !shadow_vmcs12->hdr.shadow_vmcs)
> + return -EINVAL;
> + }
> +
> if (check_vmentry_prereqs(vcpu, vmcs12) ||
> check_vmentry_postreqs(vcpu, vmcs12, &exit_qual))
> return -EINVAL;
> --
> 1.8.3.1
>