All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Roger Pau Monné" <roger.pau@citrix.com>
To: "M. Vefa Bicakci" <m.v.b@runbox.com>
Cc: <xen-devel@lists.xenproject.org>,
	<marmarek@invisiblethingslab.com>, <jbeulich@suse.com>
Subject: Re: [PATCH 1/1] x86/ept: Fix buggy XSA-321 backport
Date: Tue, 16 Feb 2021 10:20:03 +0100	[thread overview]
Message-ID: <YCuOQ3qpFD6RgIld@Air-de-Roger> (raw)
In-Reply-To: <20210215234619.245422-2-m.v.b@runbox.com>

On Mon, Feb 15, 2021 at 06:46:19PM -0500, M. Vefa Bicakci wrote:
> This commit aims to fix commit a852040fe3ab ("x86/ept: flush cache when
> modifying PTEs and sharing page tables"). The aforementioned commit is
> for the stable-4.9 branch of Xen and is a backported version of commit
> c23274fd0412 ("x86/ept: flush cache when modifying PTEs and sharing page
> tables"), which was for Xen 4.14.0-rc5 and which fixes the security
> issue reported by XSA-321.
> 
> Prior to the latter commit, the function atomic_write_ept_entry in Xen
> 4.14.y consisted mostly of a call to p2m_entry_modify followed by an
> atomic replacement of a page table entry, and the latter commit adds
> a call to iommu_sync_cache for a specific condition:
> 
>    static int atomic_write_ept_entry(struct p2m_domain *p2m,
>                                      ept_entry_t *entryptr, ept_entry_t new,
>                                      int level)
>    {
>        int rc = p2m_entry_modify(p2m, new.sa_p2mt, entryptr->sa_p2mt,
>                                  _mfn(new.mfn), _mfn(entryptr->mfn), level + 1);
> 
>        if ( rc )
>            return rc;
> 
>        write_atomic(&entryptr->epte, new.epte);
> 
>   +    /* snipped comment block */
>   +    if ( !new.recalc && iommu_use_hap_pt(p2m->domain) )
>   +        iommu_sync_cache(entryptr, sizeof(*entryptr));
>   +
>        return 0;
>    }
> 
> However, the backport to Xen 4.9.y is a bit different because
> atomic_write_ept_entry does not consist solely of a call to
> p2m_entry_modify, which does not exist in Xen 4.9.y. I am quoting from
> Xen 4.8.y for convenience:
> 
>    static int atomic_write_ept_entry(ept_entry_t *entryptr, ept_entry_t new,
>                                      int level)
>    {
>        int rc;
>        unsigned long oldmfn = mfn_x(INVALID_MFN);
>        bool_t check_foreign = (new.mfn != entryptr->mfn ||
>                                new.sa_p2mt != entryptr->sa_p2mt);
> 
>        if ( level )
>        {
>            ASSERT(!is_epte_superpage(&new) || !p2m_is_foreign(new.sa_p2mt));
>            write_atomic(&entryptr->epte, new.epte);
>            return 0;
>        }
> 
>        if ( unlikely(p2m_is_foreign(new.sa_p2mt)) )
>        {
>            rc = -EINVAL;
>            if ( !is_epte_present(&new) )
>                    goto out;
> 
>            if ( check_foreign )
>            {
>                struct domain *fdom;
> 
>                if ( !mfn_valid(new.mfn) )
>                    goto out;
> 
>                rc = -ESRCH;
>                fdom = page_get_owner(mfn_to_page(new.mfn));
>                if ( fdom == NULL )
>                    goto out;
> 
>                /* get refcount on the page */
>                rc = -EBUSY;
>                if ( !get_page(mfn_to_page(new.mfn), fdom) )
>                    goto out;
>            }
>        }
> 
>        if ( unlikely(p2m_is_foreign(entryptr->sa_p2mt)) && check_foreign )
>            oldmfn = entryptr->mfn;
> 
>        write_atomic(&entryptr->epte, new.epte);
> 
>   +    /* snipped comment block */
>   +    if ( !new.recalc && iommu_hap_pt_share )
>   +        iommu_sync_cache(entryptr, sizeof(*entryptr));
>   +
>        if ( unlikely(oldmfn != mfn_x(INVALID_MFN)) )
>            put_page(mfn_to_page(oldmfn));
> 
>        rc = 0;
> 
>     out:
>        if ( rc )
>            gdprintk(XENLOG_ERR, "epte o:%"PRIx64" n:%"PRIx64" rc:%d\n",
>                     entryptr->epte, new.epte, rc);
>        return rc;
>    }
> 
> Based on inspection of the p2m_entry_modify function in Xen 4.14.1, it
> appears that the part of atomic_write_ept_entry above the call to
> write_atomic is encapsulated by p2m_entry_modify, which uncovers one
> issue with the backport: In Xen 4.14, if p2m_entry_modify returns early
> without an error, then the calls to write_atomic and iommu_sync_cache
> will still occur (assuming that the corresponding if condition is
> satisfied), whereas in Xen 4.9.y, there is a code path that can skip the
> call to iommu_sync_cache, in case the variable level is not zero:
> 
>   if ( level )
>   {
>      ASSERT(!is_epte_superpage(&new) || !p2m_is_foreign(new.sa_p2mt));
>      write_atomic(&entryptr->epte, new.epte);
>      return 0;
>   }
> 
> This patch reorganizes the atomic_write_ept_entry to ensure that the
> call to iommu_sync_cache is not inadvertently skipped.

IMO this is likely too much change in a single patch, iff we really
wanted to do this you should have a pre-patch that re-arranges the
code without any functional change followed by a patch that fixes the
issue.

In any case I think this is too much change, so I would go for a
smaller fix like my proposal below. Can you please test it?

> Furthermore, in Xen 4.14.1, p2m_entry_modify calls
> 
>   put_page(mfn_to_page(oldmfn));
> 
> before the potential call to iommu_sync_cache in atomic_write_ept_entry.
> I am not sufficiently familiar with Xen to determine if this is a
> significant behavioural change, but this patch makes Xen 4.9.y similar
> to Xen 4.14.1 in that regard as well, by further re-organizing the code
> in atomic_write_ept_entry.

Well, that put_page is only relevant to PVH dom0, but you shouldn't
remove it. The put_page call in newer versions has been moved by
commit ce0224bf96a1a1f82 into p2m_entry_modify.

Here is my proposed fix, I think we could even do away with the else
branch, but if level is != 0 p2m_is_foreign should be false, so we
avoid an extra check.

Thanks, Roger.
---8<---
diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c
index 036771f43c..086739ffdd 100644
--- a/xen/arch/x86/mm/p2m-ept.c
+++ b/xen/arch/x86/mm/p2m-ept.c
@@ -56,11 +56,8 @@ static int atomic_write_ept_entry(ept_entry_t *entryptr, ept_entry_t new,
     if ( level )
     {
         ASSERT(!is_epte_superpage(&new) || !p2m_is_foreign(new.sa_p2mt));
-        write_atomic(&entryptr->epte, new.epte);
-        return 0;
     }
-
-    if ( unlikely(p2m_is_foreign(new.sa_p2mt)) )
+    else if ( unlikely(p2m_is_foreign(new.sa_p2mt)) )
     {
         rc = -EINVAL;
         if ( !is_epte_present(&new) )



  reply	other threads:[~2021-02-16  9:20 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-15 23:46 [PATCH 0/1] Fix for a buggy XSA-321 resolution in Xen 4.9 M. Vefa Bicakci
2021-02-15 23:46 ` [PATCH 1/1] x86/ept: Fix buggy XSA-321 backport M. Vefa Bicakci
2021-02-16  9:20   ` Roger Pau Monné [this message]
2021-02-16 12:48     ` M. Vefa Bicakci
2021-02-17  1:55       ` M. Vefa Bicakci

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=YCuOQ3qpFD6RgIld@Air-de-Roger \
    --to=roger.pau@citrix.com \
    --cc=jbeulich@suse.com \
    --cc=m.v.b@runbox.com \
    --cc=marmarek@invisiblethingslab.com \
    --cc=xen-devel@lists.xenproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.