KVM: arm64: Reject shared table walks in the hyp code
authorOliver Upton <oliver.upton@linux.dev>
Fri, 18 Nov 2022 18:22:22 +0000 (18:22 +0000)
committerMarc Zyngier <maz@kernel.org>
Tue, 22 Nov 2022 13:05:53 +0000 (13:05 +0000)
Exclusive table walks are the only supported table walk in the hyp, as
there is no construct like RCU available in the hypervisor code. Reject
any attempt to do a shared table walk by returning an error and allowing
the caller to clean up the mess.

Suggested-by: Will Deacon <will@kernel.org>
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
Acked-by: Will Deacon <will@kernel.org>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20221118182222.3932898-4-oliver.upton@linux.dev
arch/arm64/include/asm/kvm_pgtable.h
arch/arm64/kvm/hyp/pgtable.c

index 4b6b52ebc11c3e70abd1a0ea67ec995730383f29..d5cb01f8dc0669d3b7f50074b5b2f1a4d372744b 100644 (file)
@@ -229,7 +229,18 @@ static inline kvm_pte_t *kvm_dereference_pteref(struct kvm_pgtable_walker *walke
        return pteref;
 }
 
-static inline void kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker) {}
+static inline int kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker)
+{
+       /*
+        * Due to the lack of RCU (or a similar protection scheme), only
+        * non-shared table walkers are allowed in the hypervisor.
+        */
+       if (walker->flags & KVM_PGTABLE_WALK_SHARED)
+               return -EPERM;
+
+       return 0;
+}
+
 static inline void kvm_pgtable_walk_end(struct kvm_pgtable_walker *walker) {}
 
 static inline bool kvm_pgtable_walk_lock_held(void)
@@ -247,10 +258,12 @@ static inline kvm_pte_t *kvm_dereference_pteref(struct kvm_pgtable_walker *walke
        return rcu_dereference_check(pteref, !(walker->flags & KVM_PGTABLE_WALK_SHARED));
 }
 
-static inline void kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker)
+static inline int kvm_pgtable_walk_begin(struct kvm_pgtable_walker *walker)
 {
        if (walker->flags & KVM_PGTABLE_WALK_SHARED)
                rcu_read_lock();
+
+       return 0;
 }
 
 static inline void kvm_pgtable_walk_end(struct kvm_pgtable_walker *walker)
index d6f3753cb87ed600c9ba8dbff76e9a9395d2e15c..58dbe0ab567fe2e8c7f176b2f65968848b0e59fe 100644 (file)
@@ -289,7 +289,10 @@ int kvm_pgtable_walk(struct kvm_pgtable *pgt, u64 addr, u64 size,
        };
        int r;
 
-       kvm_pgtable_walk_begin(walker);
+       r = kvm_pgtable_walk_begin(walker);
+       if (r)
+               return r;
+
        r = _kvm_pgtable_walk(pgt, &walk_data);
        kvm_pgtable_walk_end(walker);