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 6bc30f6..8d99667 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 c8b79ad..645c32c 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) {