]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: s390: vsie: Fix unshadowing while shadowing
authorClaudio Imbrenda <imbrenda@linux.ibm.com>
Thu, 26 Mar 2026 13:17:17 +0000 (14:17 +0100)
committerClaudio Imbrenda <imbrenda@linux.ibm.com>
Thu, 26 Mar 2026 15:12:30 +0000 (16:12 +0100)
If shadowing causes the shadow gmap to get unshadowed, exit early to
prevent an attempt to dereference the parent pointer, which at this
point is NULL.

Opportunistically add some more checks to prevent NULL parents.

Fixes: a2c17f9270cc ("KVM: s390: New gmap code")
Fixes: e5f98a6899bd ("KVM: s390: Add some helper functions needed for vSIE")
Fixes: e38c884df921 ("KVM: s390: Switch to new gmap")
Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
arch/s390/kvm/gaccess.c
arch/s390/kvm/gmap.c

index 6bc30f67892151752dab48008227c282deb87534..8d99667e7d34a3c6720c1c22da4732533e148fb2 100644 (file)
@@ -1449,6 +1449,8 @@ static int _do_shadow_pte(struct gmap *sg, gpa_t raddr, union pte *ptep_h, union
        pgste_set_unlock(ptep_h, pgste);
        if (rc)
                return rc;
+       if (!sg->parent)
+               return -EAGAIN;
 
        newpte = _pte(f->pfn, 0, !p, 0);
        if (!pgste_get_trylock(ptep, &pgste))
@@ -1476,6 +1478,9 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni
                return rc;
 
        do {
+               /* _gmap_crstep_xchg_atomic() could have unshadowed this shadow gmap */
+               if (!sg->parent)
+                       return -EAGAIN;
                oldcrste = READ_ONCE(*host);
                newcrste = _crste_fc1(f->pfn, oldcrste.h.tt, f->writable, !p);
                newcrste.s.fc1.d |= oldcrste.s.fc1.d;
@@ -1487,6 +1492,8 @@ static int _do_shadow_crste(struct gmap *sg, gpa_t raddr, union crste *host, uni
                if (!newcrste.h.p && !f->writable)
                        return -EOPNOTSUPP;
        } while (!_gmap_crstep_xchg_atomic(sg->parent, host, oldcrste, newcrste, f->gfn, false));
+       if (!sg->parent)
+               return -EAGAIN;
 
        newcrste = _crste_fc1(f->pfn, oldcrste.h.tt, 0, !p);
        gfn = gpa_to_gfn(raddr);
@@ -1531,6 +1538,8 @@ static int _gaccess_do_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *sg,
                                       entries[i - 1].pfn, i, entries[i - 1].writable);
                if (rc)
                        return rc;
+               if (!sg->parent)
+                       return -EAGAIN;
        }
 
        rc = dat_entry_walk(NULL, entries[LEVEL_MEM].gfn, sg->parent->asce, DAT_WALK_LEAF,
index c8b79ad04ac9ea55a18086216bb72df61ebe3de1..645c32c767d24b81c401028a6679224e67e70eed 100644 (file)
@@ -1160,6 +1160,7 @@ struct gmap_protect_asce_top_level {
 static inline int __gmap_protect_asce_top_level(struct kvm_s390_mmu_cache *mc, struct gmap *sg,
                                                struct gmap_protect_asce_top_level *context)
 {
+       struct gmap *parent;
        int rc, i;
 
        guard(write_lock)(&sg->kvm->mmu_lock);
@@ -1167,7 +1168,12 @@ static inline int __gmap_protect_asce_top_level(struct kvm_s390_mmu_cache *mc, s
        if (kvm_s390_array_needs_retry_safe(sg->kvm, context->seq, context->f))
                return -EAGAIN;
 
-       scoped_guard(spinlock, &sg->parent->children_lock) {
+       parent = READ_ONCE(sg->parent);
+       if (!parent)
+               return -EAGAIN;
+       scoped_guard(spinlock, &parent->children_lock) {
+               if (READ_ONCE(sg->parent) != parent)
+                       return -EAGAIN;
                for (i = 0; i < CRST_TABLE_PAGES; i++) {
                        if (!context->f[i].valid)
                                continue;
@@ -1250,6 +1256,9 @@ struct gmap *gmap_create_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *pare
        struct gmap *sg, *new;
        int rc;
 
+       if (WARN_ON(!parent))
+               return ERR_PTR(-EINVAL);
+
        scoped_guard(spinlock, &parent->children_lock) {
                sg = gmap_find_shadow(parent, asce, edat_level);
                if (sg) {