|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH 1/1] x86/ept: Fix buggy XSA-321 backport
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) )
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |