Skip to content

Commit 7d3c27b

Browse files
Fuad Tabbagregkh
authored andcommitted
KVM: arm64: Fix pin leak and publication ordering in __pkvm_init_vcpu()
commit 73b9c1e upstream. Two bugs exist in the vCPU initialisation path: 1. If a check fails after hyp_pin_shared_mem() succeeds, the cleanup path jumps to 'unlock' without calling unpin_host_vcpu() or unpin_host_sve_state(), permanently leaking pin references on the host vCPU and SVE state pages. Extract a register_hyp_vcpu() helper that performs the checks and the store. When register_hyp_vcpu() returns an error, call unpin_host_vcpu() and unpin_host_sve_state() inline before falling through to the existing 'unlock' label. 2. register_hyp_vcpu() publishes the new vCPU pointer into 'hyp_vm->vcpus[]' with a bare store, allowing a concurrent caller of pkvm_load_hyp_vcpu() to observe a partially initialised vCPU object. Ensure the store uses smp_store_release() and the load uses smp_load_acquire(). While 'vm_table_lock' currently serialises the store and the load, these barriers ensure the reader sees the fully initialised 'hyp_vcpu' object even if there were a lockless path or if the lock's own ordering guarantees were insufficient for nested object initialization. Fixes: 49af6ddb8e5c ("KVM: arm64: Add infrastructure to create and track pKVM instances at EL2") Reported-by: Ben Simner <ben.simner@cl.cam.ac.uk> Co-developed-by: Will Deacon <willdeacon@google.com> Signed-off-by: Will Deacon <willdeacon@google.com> Signed-off-by: Fuad Tabba <tabba@google.com> Link: https://patch.msgid.link/20260424084908.370776-6-tabba@google.com Signed-off-by: Marc Zyngier <maz@kernel.org> Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 931c9c7 commit 7d3c27b

1 file changed

Lines changed: 25 additions & 13 deletions

File tree

arch/arm64/kvm/hyp/nvhe/pkvm.c

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,8 @@ struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
259259
if (!hyp_vm || hyp_vm->kvm.created_vcpus <= vcpu_idx)
260260
goto unlock;
261261

262-
hyp_vcpu = hyp_vm->vcpus[vcpu_idx];
262+
/* Pairs with smp_store_release() in register_hyp_vcpu(). */
263+
hyp_vcpu = smp_load_acquire(&hyp_vm->vcpus[vcpu_idx]);
263264
if (!hyp_vcpu)
264265
goto unlock;
265266

@@ -801,12 +802,30 @@ int __pkvm_init_vm(struct kvm *host_kvm, unsigned long vm_hva,
801802
* the page-aligned size of 'struct pkvm_hyp_vcpu'.
802803
* Return 0 on success, negative error code on failure.
803804
*/
805+
static int register_hyp_vcpu(struct pkvm_hyp_vm *hyp_vm,
806+
struct pkvm_hyp_vcpu *hyp_vcpu)
807+
{
808+
unsigned int idx = hyp_vcpu->vcpu.vcpu_idx;
809+
810+
if (idx >= hyp_vm->kvm.created_vcpus)
811+
return -EINVAL;
812+
813+
if (hyp_vm->vcpus[idx])
814+
return -EINVAL;
815+
816+
/*
817+
* Ensure the hyp_vcpu is initialised before publishing it to
818+
* the vCPU-load path via 'hyp_vm->vcpus[]'.
819+
*/
820+
smp_store_release(&hyp_vm->vcpus[idx], hyp_vcpu);
821+
return 0;
822+
}
823+
804824
int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
805825
unsigned long vcpu_hva)
806826
{
807827
struct pkvm_hyp_vcpu *hyp_vcpu;
808828
struct pkvm_hyp_vm *hyp_vm;
809-
unsigned int idx;
810829
int ret;
811830

812831
hyp_vcpu = map_donated_memory(vcpu_hva, sizeof(*hyp_vcpu));
@@ -825,18 +844,11 @@ int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
825844
if (ret)
826845
goto unlock;
827846

828-
idx = hyp_vcpu->vcpu.vcpu_idx;
829-
if (idx >= hyp_vm->kvm.created_vcpus) {
830-
ret = -EINVAL;
831-
goto unlock;
832-
}
833-
834-
if (hyp_vm->vcpus[idx]) {
835-
ret = -EINVAL;
836-
goto unlock;
847+
ret = register_hyp_vcpu(hyp_vm, hyp_vcpu);
848+
if (ret) {
849+
unpin_host_vcpu(host_vcpu);
850+
unpin_host_sve_state(hyp_vcpu);
837851
}
838-
839-
hyp_vm->vcpus[idx] = hyp_vcpu;
840852
unlock:
841853
hyp_spin_unlock(&vm_table_lock);
842854

0 commit comments

Comments
 (0)