Skip to content

Commit 292a7d6

Browse files
author
Claudio Imbrenda
committed
KVM: s390: pv: fix asynchronous teardown for small VMs
On machines without the Destroy Secure Configuration Fast UVC, the topmost level of page tables is set aside and freed asynchronously as last step of the asynchronous teardown. Each gmap has a host_to_guest radix tree mapping host (userspace) addresses (with 1M granularity) to gmap segment table entries (pmds). If a guest is smaller than 2GB, the topmost level of page tables is the segment table (i.e. there are only 2 levels). Replacing it means that the pointers in the host_to_guest mapping would become stale and cause all kinds of nasty issues. This patch fixes the issue by disallowing asynchronous teardown for guests with only 2 levels of page tables. Userspace should (and already does) try using the normal destroy if the asynchronous one fails. Update s390_replace_asce so it refuses to replace segment type ASCEs. This is still needed in case the normal destroy VM fails. Fixes: fb491d5 ("KVM: s390: pv: asynchronous destroy for reboot") Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com> Reviewed-by: Janosch Frank <frankja@linux.ibm.com> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com> Message-Id: <20230421085036.52511-2-imbrenda@linux.ibm.com>
1 parent 8a46df7 commit 292a7d6

2 files changed

Lines changed: 12 additions & 0 deletions

File tree

arch/s390/kvm/pv.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,11 @@ int kvm_s390_pv_set_aside(struct kvm *kvm, u16 *rc, u16 *rrc)
314314
*/
315315
if (kvm->arch.pv.set_aside)
316316
return -EINVAL;
317+
318+
/* Guest with segment type ASCE, refuse to destroy asynchronously */
319+
if ((kvm->arch.gmap->asce & _ASCE_TYPE_MASK) == _ASCE_TYPE_SEGMENT)
320+
return -EINVAL;
321+
317322
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
318323
if (!priv)
319324
return -ENOMEM;

arch/s390/mm/gmap.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2833,6 +2833,9 @@ EXPORT_SYMBOL_GPL(s390_unlist_old_asce);
28332833
* s390_replace_asce - Try to replace the current ASCE of a gmap with a copy
28342834
* @gmap: the gmap whose ASCE needs to be replaced
28352835
*
2836+
* If the ASCE is a SEGMENT type then this function will return -EINVAL,
2837+
* otherwise the pointers in the host_to_guest radix tree will keep pointing
2838+
* to the wrong pages, causing use-after-free and memory corruption.
28362839
* If the allocation of the new top level page table fails, the ASCE is not
28372840
* replaced.
28382841
* In any case, the old ASCE is always removed from the gmap CRST list.
@@ -2847,6 +2850,10 @@ int s390_replace_asce(struct gmap *gmap)
28472850

28482851
s390_unlist_old_asce(gmap);
28492852

2853+
/* Replacing segment type ASCEs would cause serious issues */
2854+
if ((gmap->asce & _ASCE_TYPE_MASK) == _ASCE_TYPE_SEGMENT)
2855+
return -EINVAL;
2856+
28502857
page = alloc_pages(GFP_KERNEL_ACCOUNT, CRST_ALLOC_ORDER);
28512858
if (!page)
28522859
return -ENOMEM;

0 commit comments

Comments
 (0)