The virtual-mode HPT hcall handlers (H_ENTER, H_REMOVE, H_BULK_REMOVE,
H_CLEAR_REF, H_CLEAR_MOD) call into book3s_hv_rm_mmu.c, which accesses
memslots via kvm_memslots_raw().  kvm_memslots_raw() uses
rcu_dereference_raw_check() to bypass SRCU lockdep annotation checking.
This is safe in real mode because the entire guest entry/exit is wrapped
in srcu_read_lock/unlock inside kvmppc_run_core() and
kvmhv_run_single_vcpu().

However, since commit 6165d5dd99db ("KVM: PPC: Book3S HV: add virtual
mode handlers for HPT hcalls and page faults"), these same handlers are
also executed in virtual mode via kvmppc_pseries_do_hcall(), which runs
after SRCU has already been released on guest exit.

A concurrent KVM_SET_USER_MEMORY_REGION deletion or move can therefore
call synchronize_srcu_expedited() — which does not wait for this thread —
and then kfree(slot) and vfree(slot->arch.rmap) while the hcall handler
still holds a raw pointer to the memslot.  lock_rmap() then writes to
freed vmalloc memory, leading to use-after-free and memory corruption.
Because kvm_memslots_raw() suppresses lockdep checks, this race is
entirely silent.

Fix this by factoring out the 7 HPT hcall handlers (H_REMOVE, H_ENTER,
H_READ, H_CLEAR_MOD, H_CLEAR_REF, H_PROTECT, H_BULK_REMOVE) into a
helper function, kvmppc_pseries_do_hpt_hcall(), and wrapping its call
site in srcu_read_lock(&kvm->srcu) / srcu_read_unlock(&kvm->srcu, idx).
Targeting only the HPT hcalls avoids wrapping non-HPT hcalls that sleep
(such as H_CONFER, H_REGISTER_VPA, H_PAGE_INIT) or handlers that already
acquire SRCU internally (such as H_RTAS).

Fixes: 6165d5dd99db ("KVM: PPC: Book3S HV: add virtual mode handlers for HPT 
hcalls and page faults")
Cc: [email protected] # v5.14+
Suggested-by: Ritesh Harjani (IBM) <[email protected]>
Signed-off-by: Amit Machhiwal <[email protected]>
---
 arch/powerpc/kvm/book3s_hv.c | 70 ++++++++++++++++++------------------
 1 file changed, 35 insertions(+), 35 deletions(-)

diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index dbac3573b2c8..aa51968e206a 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -1159,6 +1159,38 @@ static long kvmppc_h_rpt_invalidate(struct kvm_vcpu 
*vcpu,
        return H_SUCCESS;
 }
 
+static long kvmppc_pseries_do_hpt_hcall(struct kvm_vcpu *vcpu, unsigned long 
req)
+{
+       switch (req) {
+       case H_REMOVE:
+               return kvmppc_h_remove(vcpu, kvmppc_get_gpr(vcpu, 4),
+                                      kvmppc_get_gpr(vcpu, 5),
+                                      kvmppc_get_gpr(vcpu, 6));
+       case H_ENTER:
+               return kvmppc_h_enter(vcpu, kvmppc_get_gpr(vcpu, 4),
+                                     kvmppc_get_gpr(vcpu, 5),
+                                     kvmppc_get_gpr(vcpu, 6),
+                                     kvmppc_get_gpr(vcpu, 7));
+       case H_READ:
+               return kvmppc_h_read(vcpu, kvmppc_get_gpr(vcpu, 4),
+                                    kvmppc_get_gpr(vcpu, 5));
+       case H_CLEAR_MOD:
+               return kvmppc_h_clear_mod(vcpu, kvmppc_get_gpr(vcpu, 4),
+                                         kvmppc_get_gpr(vcpu, 5));
+       case H_CLEAR_REF:
+               return kvmppc_h_clear_ref(vcpu, kvmppc_get_gpr(vcpu, 4),
+                                         kvmppc_get_gpr(vcpu, 5));
+       case H_PROTECT:
+               return kvmppc_h_protect(vcpu, kvmppc_get_gpr(vcpu, 4),
+                                       kvmppc_get_gpr(vcpu, 5),
+                                       kvmppc_get_gpr(vcpu, 6));
+       case H_BULK_REMOVE:
+               return kvmppc_h_bulk_remove(vcpu);
+       }
+
+       return H_FUNCTION;
+}
+
 int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
 {
        struct kvm *kvm = vcpu->kvm;
@@ -1174,47 +1206,15 @@ int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu)
 
        switch (req) {
        case H_REMOVE:
-               ret = kvmppc_h_remove(vcpu, kvmppc_get_gpr(vcpu, 4),
-                                       kvmppc_get_gpr(vcpu, 5),
-                                       kvmppc_get_gpr(vcpu, 6));
-               if (ret == H_TOO_HARD)
-                       return RESUME_HOST;
-               break;
        case H_ENTER:
-               ret = kvmppc_h_enter(vcpu, kvmppc_get_gpr(vcpu, 4),
-                                       kvmppc_get_gpr(vcpu, 5),
-                                       kvmppc_get_gpr(vcpu, 6),
-                                       kvmppc_get_gpr(vcpu, 7));
-               if (ret == H_TOO_HARD)
-                       return RESUME_HOST;
-               break;
        case H_READ:
-               ret = kvmppc_h_read(vcpu, kvmppc_get_gpr(vcpu, 4),
-                                       kvmppc_get_gpr(vcpu, 5));
-               if (ret == H_TOO_HARD)
-                       return RESUME_HOST;
-               break;
        case H_CLEAR_MOD:
-               ret = kvmppc_h_clear_mod(vcpu, kvmppc_get_gpr(vcpu, 4),
-                                       kvmppc_get_gpr(vcpu, 5));
-               if (ret == H_TOO_HARD)
-                       return RESUME_HOST;
-               break;
        case H_CLEAR_REF:
-               ret = kvmppc_h_clear_ref(vcpu, kvmppc_get_gpr(vcpu, 4),
-                                       kvmppc_get_gpr(vcpu, 5));
-               if (ret == H_TOO_HARD)
-                       return RESUME_HOST;
-               break;
        case H_PROTECT:
-               ret = kvmppc_h_protect(vcpu, kvmppc_get_gpr(vcpu, 4),
-                                       kvmppc_get_gpr(vcpu, 5),
-                                       kvmppc_get_gpr(vcpu, 6));
-               if (ret == H_TOO_HARD)
-                       return RESUME_HOST;
-               break;
        case H_BULK_REMOVE:
-               ret = kvmppc_h_bulk_remove(vcpu);
+               idx = srcu_read_lock(&kvm->srcu);
+               ret = kvmppc_pseries_do_hpt_hcall(vcpu, req);
+               srcu_read_unlock(&kvm->srcu, idx);
                if (ret == H_TOO_HARD)
                        return RESUME_HOST;
                break;
-- 
2.54.0 (Apple Git-157)


Reply via email to