Skip to content

Commit 5f230f4

Browse files
davidhildenbrandClaudio Imbrenda
authored andcommitted
KVM: s390: vsie: fix some corner-cases when grabbing vsie pages
We try to reuse the same vsie page when re-executing the vsie with a given SCB address. The result is that we use the same shadow SCB -- residing in the vsie page -- and can avoid flushing the TLB when re-running the vsie on a CPU. So, when we allocate a fresh vsie page, or when we reuse a vsie page for a different SCB address -- reusing the shadow SCB in different context -- we set ihcpu=0xffff to trigger the flush. However, after we looked up the SCB address in the radix tree, but before we grabbed the vsie page by raising the refcount to 2, someone could reuse the vsie page for a different SCB address, adjusting page->index and the radix tree. In that case, we would be reusing the vsie page with a wrong page->index. Another corner case is that we might set the SCB address for a vsie page, but fail the insertion into the radix tree. Whoever would reuse that page would remove the corresponding radix tree entry -- which might now be a valid entry pointing at another page, resulting in the wrong vsie page getting removed from the radix tree. Let's handle such races better, by validating that the SCB address of a vsie page didn't change after we grabbed it (not reuse for a different SCB; the alternative would be performing another tree lookup), and by setting the SCB address to invalid until the insertion in the tree succeeded (SCB addresses are aligned to 512, so ULONG_MAX is invalid). These scenarios are rare, the effects a bit unclear, and these issues were only found by code inspection. Let's CC stable to be safe. Fixes: a3508fb ("KVM: s390: vsie: initial support for nested virtualization") Cc: stable@vger.kernel.org Signed-off-by: David Hildenbrand <david@redhat.com> Reviewed-by: Claudio Imbrenda <imbrenda@linux.ibm.com> Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com> Tested-by: Christoph Schlameuss <schlameuss@linux.ibm.com> Message-ID: <20250107154344.1003072-2-david@redhat.com> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
1 parent 72deda0 commit 5f230f4

File tree

1 file changed

+19
-6
lines changed

1 file changed

+19
-6
lines changed

arch/s390/kvm/vsie.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,8 +1362,14 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
13621362
page = radix_tree_lookup(&kvm->arch.vsie.addr_to_page, addr >> 9);
13631363
rcu_read_unlock();
13641364
if (page) {
1365-
if (page_ref_inc_return(page) == 2)
1366-
return page_to_virt(page);
1365+
if (page_ref_inc_return(page) == 2) {
1366+
if (page->index == addr)
1367+
return page_to_virt(page);
1368+
/*
1369+
* We raced with someone reusing + putting this vsie
1370+
* page before we grabbed it.
1371+
*/
1372+
}
13671373
page_ref_dec(page);
13681374
}
13691375

@@ -1393,15 +1399,20 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
13931399
kvm->arch.vsie.next++;
13941400
kvm->arch.vsie.next %= nr_vcpus;
13951401
}
1396-
radix_tree_delete(&kvm->arch.vsie.addr_to_page, page->index >> 9);
1402+
if (page->index != ULONG_MAX)
1403+
radix_tree_delete(&kvm->arch.vsie.addr_to_page,
1404+
page->index >> 9);
13971405
}
1398-
page->index = addr;
1399-
/* double use of the same address */
1406+
/* Mark it as invalid until it resides in the tree. */
1407+
page->index = ULONG_MAX;
1408+
1409+
/* Double use of the same address or allocation failure. */
14001410
if (radix_tree_insert(&kvm->arch.vsie.addr_to_page, addr >> 9, page)) {
14011411
page_ref_dec(page);
14021412
mutex_unlock(&kvm->arch.vsie.mutex);
14031413
return NULL;
14041414
}
1415+
page->index = addr;
14051416
mutex_unlock(&kvm->arch.vsie.mutex);
14061417

14071418
vsie_page = page_to_virt(page);
@@ -1496,7 +1507,9 @@ void kvm_s390_vsie_destroy(struct kvm *kvm)
14961507
vsie_page = page_to_virt(page);
14971508
release_gmap_shadow(vsie_page);
14981509
/* free the radix tree entry */
1499-
radix_tree_delete(&kvm->arch.vsie.addr_to_page, page->index >> 9);
1510+
if (page->index != ULONG_MAX)
1511+
radix_tree_delete(&kvm->arch.vsie.addr_to_page,
1512+
page->index >> 9);
15001513
__free_page(page);
15011514
}
15021515
kvm->arch.vsie.page_count = 0;

0 commit comments

Comments
 (0)