ANDROID: KVM: arm64: Strictly check page type in MEM_RELINQUISH hypercall

The VM should only relinquish "normal" pages. For a protected VM, this
means PAGE_OWNED; For a normal VM, this means PAGE_SHARED_BORROWED. All
other page types are rejected and failure is reported to the caller.

Bug: 259217067
Change-Id: Icff3474dc2c975a6c5befe546c5521a05b3bd575
Signed-off-by: Keir Fraser <keirf@google.com>
This commit is contained in:
Keir Fraser
2022-11-16 10:48:54 +00:00
parent 4a31e02b6c
commit a75533eb44

View File

@@ -265,7 +265,6 @@ static int reclaim_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
{
kvm_pte_t pte = *ptep;
struct hyp_page *page;
u64 *pa = arg;
if (!kvm_pte_valid(pte))
return 0;
@@ -277,8 +276,6 @@ static int reclaim_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
fallthrough;
case PKVM_PAGE_SHARED_BORROWED:
case PKVM_PAGE_SHARED_OWNED:
if (pa)
*pa = kvm_pte_to_phys(pte);
page->flags |= HOST_PAGE_PENDING_RECLAIM;
break;
default:
@@ -318,13 +315,44 @@ void reclaim_guest_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc)
}
}
struct relinquish_data {
enum pkvm_page_state expected_state;
u64 pa;
};
static int relinquish_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
enum kvm_pgtable_walk_flags flag, void * const arg)
{
kvm_pte_t pte = *ptep;
struct hyp_page *page;
struct relinquish_data *data = arg;
enum pkvm_page_state state;
if (!kvm_pte_valid(pte))
return 0;
state = pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte));
if (state != data->expected_state)
return -EPERM;
page = hyp_phys_to_page(kvm_pte_to_phys(pte));
if (state == PKVM_PAGE_OWNED)
page->flags |= HOST_PAGE_NEED_POISONING;
page->flags |= HOST_PAGE_PENDING_RECLAIM;
data->pa = kvm_pte_to_phys(pte);
return 0;
}
int __pkvm_guest_relinquish_to_host(struct pkvm_hyp_vcpu *vcpu,
u64 ipa, u64 *ppa)
{
struct relinquish_data data;
struct kvm_pgtable_walker walker = {
.cb = reclaim_walker,
.arg = ppa,
.flags = KVM_PGTABLE_WALK_LEAF
.cb = relinquish_walker,
.flags = KVM_PGTABLE_WALK_LEAF,
.arg = &data,
};
struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(vcpu);
int ret;
@@ -332,8 +360,13 @@ int __pkvm_guest_relinquish_to_host(struct pkvm_hyp_vcpu *vcpu,
host_lock_component();
guest_lock_component(vm);
/* Expected page state depends on VM type. */
data.expected_state = pkvm_hyp_vcpu_is_protected(vcpu) ?
PKVM_PAGE_OWNED :
PKVM_PAGE_SHARED_BORROWED;
/* Set default pa value to "not found". */
*ppa = 0;
data.pa = 0;
/* If ipa is mapped: sets page flags, and gets the pa. */
ret = kvm_pgtable_walk(&vm->pgt, ipa, PAGE_SIZE, &walker);
@@ -345,6 +378,7 @@ int __pkvm_guest_relinquish_to_host(struct pkvm_hyp_vcpu *vcpu,
guest_unlock_component(vm);
host_unlock_component();
*ppa = data.pa;
return ret;
}