After an unshare call from the guest, check that the memory isn't mapped by the host before returning to the guest. If the host has acknowledged the unsharing, by marking the memory as private, but hasn't unmapped it, return an error to the host. On the other hand if the host has not acknowledged the unsharing, then return to the guest with an error, indicating that the unsharing has failed. Signed-off-by: Fuad Tabba <tabba@xxxxxxxxxx> --- arch/arm64/kvm/hypercalls.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c index 56fb4fa70eec..23237ca400ec 100644 --- a/arch/arm64/kvm/hypercalls.c +++ b/arch/arm64/kvm/hypercalls.c @@ -24,8 +24,45 @@ f; \ }) +static int kvm_handle_unshare_return(struct kvm_vcpu *vcpu) +{ + gpa_t gpa = vcpu->run->hypercall.args[0]; + gfn_t gfn = gpa_to_gfn(gpa); + struct kvm *kvm = vcpu->kvm; + + if (!IS_ENABLED(CONFIG_KVM_PRIVATE_MEM)) + return 1; + + if (!kvm_mem_is_private(kvm, gfn)) { + /* Inform the guest that host refused to unshare the memory. */ + vcpu->run->hypercall.ret = SMCCC_RET_INVALID_PARAMETER; + WARN_ON(kvm_vm_set_mem_attributes_kernel(vcpu->kvm, gfn, gfn + 1, 0)); + + return 1; + } + + /* + * Host has acknowledged that the memory has been unshared by marking it + * as private, so check if it still has mapping. If it does, exit back + * to the host to fix it. + * The exit reason should still be preserved. + */ + if (kvm_is_gmem_mapped(kvm, gfn, gfn + 1)) + return -EPERM; + + return 1; +} + int kvm_handle_hypercall_return(struct kvm_vcpu *vcpu) { + if (vcpu->run->hypercall.ret == SMCCC_RET_SUCCESS && + vcpu->run->hypercall.nr == ARM_SMCCC_KVM_FUNC_MEM_UNSHARE) { + int ret = kvm_handle_unshare_return(vcpu); + + if (ret <= 0) + return ret; + } + smccc_set_retval(vcpu, vcpu->run->hypercall.ret, vcpu->run->hypercall.args[0], vcpu->run->hypercall.args[1], -- 2.44.0.rc1.240.g4c46232300-goog