diff mbox series

[v10,02/19] KVM: s390: pv: handle secure storage violations for protected guests

Message ID 20220414080311.1084834-3-imbrenda@linux.ibm.com (mailing list archive)
State New, archived
Headers show
Series KVM: s390: pv: implement lazy destroy for reboot | expand

Commit Message

Claudio Imbrenda April 14, 2022, 8:02 a.m. UTC
With upcoming patches, protected guests will be able to trigger secure
storage violations in normal operation.

A secure storage violation is triggered when a protected guest tries to
access secure memory that has been mapped erroneously, or that belongs
to a different protected guest or to the ultravisor.

With upcoming patches, protected guests will be able to trigger secure
storage violations in normal operation. This happens for example if a
protected guest is rebooted with lazy destroy enabled and the new guest
is also protected.

When the new protected guest touches pages that have not yet been
destroyed, and thus are accounted to the previous protected guest, a
secure storage violation is raised.

This patch adds handling of secure storage violations for protected
guests.

This exception is handled by first trying to destroy the page, because
it is expected to belong to a defunct protected guest where a destroy
should be possible. If that fails, a normal export of the page is
attempted.

Therefore, pages that trigger the exception will be made non-secure
before attempting to use them again for a different secure guest.

Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Acked-by: Janosch Frank <frankja@linux.ibm.com>
---
 arch/s390/include/asm/uv.h |  1 +
 arch/s390/kernel/uv.c      | 55 ++++++++++++++++++++++++++++++++++++++
 arch/s390/mm/fault.c       | 10 +++++++
 3 files changed, 66 insertions(+)

Comments

Thomas Huth May 5, 2022, 5:10 p.m. UTC | #1
On 14/04/2022 10.02, Claudio Imbrenda wrote:
> With upcoming patches, protected guests will be able to trigger secure
> storage violations in normal operation.
> 
> A secure storage violation is triggered when a protected guest tries to
> access secure memory that has been mapped erroneously, or that belongs
> to a different protected guest or to the ultravisor.
> 
> With upcoming patches, protected guests will be able to trigger secure
> storage violations in normal operation.

You've already used this sentence as 1st sentence of the patch description. 
Looks weird to read it again. Maybe scratch the 1st sentence?

> This happens for example if a
> protected guest is rebooted with lazy destroy enabled and the new guest
> is also protected.
> 
> When the new protected guest touches pages that have not yet been
> destroyed, and thus are accounted to the previous protected guest, a
> secure storage violation is raised.
> 
> This patch adds handling of secure storage violations for protected
> guests.
> 
> This exception is handled by first trying to destroy the page, because
> it is expected to belong to a defunct protected guest where a destroy
> should be possible. If that fails, a normal export of the page is
> attempted.
 >
> Therefore, pages that trigger the exception will be made non-secure
> before attempting to use them again for a different secure guest.

I'm an complete ignorant here, but isn't this somewhat dangerous? Could it 
happen that a VM could destroy/export the pages of another secure guest that 
way?

  Thomas
Claudio Imbrenda May 6, 2022, 11:33 a.m. UTC | #2
On Thu, 5 May 2022 19:10:39 +0200
Thomas Huth <thuth@redhat.com> wrote:

> On 14/04/2022 10.02, Claudio Imbrenda wrote:
> > With upcoming patches, protected guests will be able to trigger secure
> > storage violations in normal operation.
> > 
> > A secure storage violation is triggered when a protected guest tries to
> > access secure memory that has been mapped erroneously, or that belongs
> > to a different protected guest or to the ultravisor.
> > 
> > With upcoming patches, protected guests will be able to trigger secure
> > storage violations in normal operation.  
> 
> You've already used this sentence as 1st sentence of the patch description. 
> Looks weird to read it again. Maybe scratch the 1st sentence?

oops!

> 
> > This happens for example if a
> > protected guest is rebooted with lazy destroy enabled and the new guest
> > is also protected.
> > 
> > When the new protected guest touches pages that have not yet been
> > destroyed, and thus are accounted to the previous protected guest, a
> > secure storage violation is raised.
> > 
> > This patch adds handling of secure storage violations for protected
> > guests.
> > 
> > This exception is handled by first trying to destroy the page, because
> > it is expected to belong to a defunct protected guest where a destroy
> > should be possible. If that fails, a normal export of the page is
> > attempted.
>  >
> > Therefore, pages that trigger the exception will be made non-secure
> > before attempting to use them again for a different secure guest.  
> 
> I'm an complete ignorant here, but isn't this somewhat dangerous? Could it 
> happen that a VM could destroy/export the pages of another secure guest that 
> way?

this is a good question, perhaps I should add a comment explaining that
the destroy page UVC will only work on protected VMs with no CPUs.

Exporting instead is not an issue, if/when the page is needed, it will
get imported again. Unless some things went really wrong, but that can
only happen in case of a bug in the hypervisor.

> 
>   Thomas
>
diff mbox series

Patch

diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h
index a2d376b8bce3..b96c1cf750a5 100644
--- a/arch/s390/include/asm/uv.h
+++ b/arch/s390/include/asm/uv.h
@@ -357,6 +357,7 @@  static inline int is_prot_virt_host(void)
 }
 
 int gmap_make_secure(struct gmap *gmap, unsigned long gaddr, void *uvcb);
+int gmap_destroy_page(struct gmap *gmap, unsigned long gaddr);
 int uv_destroy_owned_page(unsigned long paddr);
 int uv_convert_from_secure(unsigned long paddr);
 int uv_convert_owned_from_secure(unsigned long paddr);
diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c
index a5425075dd25..2754471cc789 100644
--- a/arch/s390/kernel/uv.c
+++ b/arch/s390/kernel/uv.c
@@ -334,6 +334,61 @@  int gmap_convert_to_secure(struct gmap *gmap, unsigned long gaddr)
 }
 EXPORT_SYMBOL_GPL(gmap_convert_to_secure);
 
+/**
+ * gmap_destroy_page - Destroy a guest page.
+ * @gmap the gmap of the guest
+ * @gaddr the guest address to destroy
+ *
+ * An attempt will be made to destroy the given guest page. If the attempt
+ * fails, an attempt is made to export the page. If both attempts fail, an
+ * appropriate error is returned.
+ */
+int gmap_destroy_page(struct gmap *gmap, unsigned long gaddr)
+{
+	struct vm_area_struct *vma;
+	unsigned long uaddr;
+	struct page *page;
+	int rc;
+
+	rc = -EFAULT;
+	mmap_read_lock(gmap->mm);
+
+	uaddr = __gmap_translate(gmap, gaddr);
+	if (IS_ERR_VALUE(uaddr))
+		goto out;
+	vma = vma_lookup(gmap->mm, uaddr);
+	if (!vma)
+		goto out;
+	/*
+	 * Huge pages should not be able to become secure
+	 */
+	if (is_vm_hugetlb_page(vma))
+		goto out;
+
+	rc = 0;
+	/* we take an extra reference here */
+	page = follow_page(vma, uaddr, FOLL_WRITE | FOLL_GET);
+	if (IS_ERR_OR_NULL(page))
+		goto out;
+	rc = uv_destroy_owned_page(page_to_phys(page));
+	/*
+	 * Fault handlers can race; it is possible that two CPUs will fault
+	 * on the same secure page. One CPU can destroy the page, reboot,
+	 * re-enter secure mode and import it, while the second CPU was
+	 * stuck at the beginning of the handler. At some point the second
+	 * CPU will be able to progress, and it will not be able to destroy
+	 * the page. In that case we do not want to terminate the process,
+	 * we instead try to export the page.
+	 */
+	if (rc)
+		rc = uv_convert_owned_from_secure(page_to_phys(page));
+	put_page(page);
+out:
+	mmap_read_unlock(gmap->mm);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(gmap_destroy_page);
+
 /*
  * To be called with the page locked or with an extra reference! This will
  * prevent gmap_make_secure from touching the page concurrently. Having 2
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
index e173b6187ad5..af1ac49168fb 100644
--- a/arch/s390/mm/fault.c
+++ b/arch/s390/mm/fault.c
@@ -837,6 +837,16 @@  NOKPROBE_SYMBOL(do_non_secure_storage_access);
 
 void do_secure_storage_violation(struct pt_regs *regs)
 {
+	unsigned long gaddr = regs->int_parm_long & __FAIL_ADDR_MASK;
+	struct gmap *gmap = (struct gmap *)S390_lowcore.gmap;
+
+	/*
+	 * If the VM has been rebooted, its address space might still contain
+	 * secure pages from the previous boot.
+	 * Clear the page so it can be reused.
+	 */
+	if (!gmap_destroy_page(gmap, gaddr))
+		return;
 	/*
 	 * Either KVM messed up the secure guest mapping or the same
 	 * page is mapped into multiple secure guests.