Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

KVM: arm64: Fix pin leak and publication ordering in __pkvm_init_vcpu()

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

authored by

Fuad Tabba and committed by
Marc Zyngier
73b9c1e5 d89fdda7

+25 -13
+25 -13
arch/arm64/kvm/hyp/nvhe/pkvm.c
··· 266 266 if (hyp_vm->kvm.created_vcpus <= vcpu_idx) 267 267 goto unlock; 268 268 269 - hyp_vcpu = hyp_vm->vcpus[vcpu_idx]; 269 + /* Pairs with smp_store_release() in register_hyp_vcpu(). */ 270 + hyp_vcpu = smp_load_acquire(&hyp_vm->vcpus[vcpu_idx]); 270 271 if (!hyp_vcpu) 271 272 goto unlock; 272 273 ··· 861 860 * the page-aligned size of 'struct pkvm_hyp_vcpu'. 862 861 * Return 0 on success, negative error code on failure. 863 862 */ 863 + static int register_hyp_vcpu(struct pkvm_hyp_vm *hyp_vm, 864 + struct pkvm_hyp_vcpu *hyp_vcpu) 865 + { 866 + unsigned int idx = hyp_vcpu->vcpu.vcpu_idx; 867 + 868 + if (idx >= hyp_vm->kvm.created_vcpus) 869 + return -EINVAL; 870 + 871 + if (hyp_vm->vcpus[idx]) 872 + return -EINVAL; 873 + 874 + /* 875 + * Ensure the hyp_vcpu is initialised before publishing it to 876 + * the vCPU-load path via 'hyp_vm->vcpus[]'. 877 + */ 878 + smp_store_release(&hyp_vm->vcpus[idx], hyp_vcpu); 879 + return 0; 880 + } 881 + 864 882 int __pkvm_init_vcpu(pkvm_handle_t handle, struct kvm_vcpu *host_vcpu, 865 883 unsigned long vcpu_hva) 866 884 { 867 885 struct pkvm_hyp_vcpu *hyp_vcpu; 868 886 struct pkvm_hyp_vm *hyp_vm; 869 - unsigned int idx; 870 887 int ret; 871 888 872 889 hyp_vcpu = map_donated_memory(vcpu_hva, sizeof(*hyp_vcpu)); ··· 903 884 if (ret) 904 885 goto unlock; 905 886 906 - idx = hyp_vcpu->vcpu.vcpu_idx; 907 - if (idx >= hyp_vm->kvm.created_vcpus) { 908 - ret = -EINVAL; 909 - goto unlock; 887 + ret = register_hyp_vcpu(hyp_vm, hyp_vcpu); 888 + if (ret) { 889 + unpin_host_vcpu(host_vcpu); 890 + unpin_host_sve_state(hyp_vcpu); 910 891 } 911 - 912 - if (hyp_vm->vcpus[idx]) { 913 - ret = -EINVAL; 914 - goto unlock; 915 - } 916 - 917 - hyp_vm->vcpus[idx] = hyp_vcpu; 918 892 unlock: 919 893 hyp_spin_unlock(&vm_table_lock); 920 894