Skip to content

Commit 6d69c0e

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 b03b16a commit 6d69c0e

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
@@ -258,7 +258,8 @@ struct pkvm_hyp_vcpu *pkvm_load_hyp_vcpu(pkvm_handle_t handle,
258258
if (!hyp_vm || hyp_vm->kvm.created_vcpus <= vcpu_idx)
259259
goto unlock;
260260

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

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

814833
hyp_vcpu = map_donated_memory(vcpu_hva, sizeof(*hyp_vcpu));
@@ -827,18 +846,11 @@ int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu,
827846
if (ret)
828847
goto unlock;
829848

830-
idx = hyp_vcpu->vcpu.vcpu_idx;
831-
if (idx >= hyp_vm->kvm.created_vcpus) {
832-
ret = -EINVAL;
833-
goto unlock;
834-
}
835-
836-
if (hyp_vm->vcpus[idx]) {
837-
ret = -EINVAL;
838-
goto unlock;
849+
ret = register_hyp_vcpu(hyp_vm, hyp_vcpu);
850+
if (ret) {
851+
unpin_host_vcpu(host_vcpu);
852+
unpin_host_sve_state(hyp_vcpu);
839853
}
840-
841-
hyp_vm->vcpus[idx] = hyp_vcpu;
842854
unlock:
843855
hyp_spin_unlock(&vm_table_lock);
844856

0 commit comments

Comments
 (0)