From: Andrew Cooper Date: Tue, 20 Jun 2017 12:36:11 +0000 (+0200) Subject: x86/shadow: hold references for the duration of emulated writes X-Git-Tag: archive/raspbian/4.11.1-1+rpi1~1^2~66^2~1977 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=26217aff67ae1538d4e1b2226afab6993cdbe772;p=xen.git x86/shadow: hold references for the duration of emulated writes The (misnamed) emulate_gva_to_mfn() function translates a linear address to an mfn, but releases its page reference before returning the mfn to its caller. sh_emulate_map_dest() uses the results of one or two translations to construct a virtual mapping to the underlying frames, completes an emulated write/cmpxchg, then unmaps the virtual mappings. The page references need holding until the mappings are unmapped, or the frames can change ownership before the writes occurs. This is XSA-219. Signed-off-by: Andrew Cooper Reviewed-by: Jan Beulich Reviewed-by: Tim Deegan --- diff --git a/xen/arch/x86/mm/shadow/common.c b/xen/arch/x86/mm/shadow/common.c index d4321981c2..2e64a774d8 100644 --- a/xen/arch/x86/mm/shadow/common.c +++ b/xen/arch/x86/mm/shadow/common.c @@ -1627,7 +1627,10 @@ static unsigned int shadow_get_allocation(struct domain *d) /**************************************************************************/ /* Handling guest writes to pagetables. */ -/* Translate a VA to an MFN, injecting a page-fault if we fail. */ +/* + * Translate a VA to an MFN, injecting a page-fault if we fail. If the + * mapping succeeds, a reference will be held on the underlying page. + */ #define BAD_GVA_TO_GFN (~0UL) #define BAD_GFN_TO_MFN (~1UL) #define READONLY_GFN (~2UL) @@ -1673,16 +1676,15 @@ static mfn_t emulate_gva_to_mfn(struct vcpu *v, unsigned long vaddr, ASSERT(mfn_valid(mfn)); v->arch.paging.last_write_was_pt = !!sh_mfn_is_a_page_table(mfn); - /* - * Note shadow cannot page out or unshare this mfn, so the map won't - * disappear. Otherwise, caller must hold onto page until done. - */ - put_page(page); return mfn; } -/* Check that the user is allowed to perform this write. */ +/* + * Check that the user is allowed to perform this write. If a mapping is + * returned, page references will be held on sh_ctxt->mfn[0] and + * sh_ctxt->mfn[1] iff !INVALID_MFN. + */ void *sh_emulate_map_dest(struct vcpu *v, unsigned long vaddr, unsigned int bytes, struct sh_emulate_ctxt *sh_ctxt) @@ -1690,13 +1692,6 @@ void *sh_emulate_map_dest(struct vcpu *v, unsigned long vaddr, struct domain *d = v->domain; void *map; - sh_ctxt->mfn[0] = emulate_gva_to_mfn(v, vaddr, sh_ctxt); - if ( !mfn_valid(sh_ctxt->mfn[0]) ) - return ((mfn_x(sh_ctxt->mfn[0]) == BAD_GVA_TO_GFN) ? - MAPPING_EXCEPTION : - (mfn_x(sh_ctxt->mfn[0]) == READONLY_GFN) ? - MAPPING_SILENT_FAIL : MAPPING_UNHANDLEABLE); - #ifndef NDEBUG /* We don't emulate user-mode writes to page tables. */ if ( is_hvm_domain(d) ? hvm_get_cpl(v) == 3 @@ -1708,6 +1703,17 @@ void *sh_emulate_map_dest(struct vcpu *v, unsigned long vaddr, } #endif + sh_ctxt->mfn[0] = emulate_gva_to_mfn(v, vaddr, sh_ctxt); + if ( !mfn_valid(sh_ctxt->mfn[0]) ) + { + switch ( mfn_x(sh_ctxt->mfn[0]) ) + { + case BAD_GVA_TO_GFN: return MAPPING_EXCEPTION; + case READONLY_GFN: return MAPPING_SILENT_FAIL; + default: return MAPPING_UNHANDLEABLE; + } + } + /* Unaligned writes mean probably this isn't a pagetable. */ if ( vaddr & (bytes - 1) ) sh_remove_shadows(d, sh_ctxt->mfn[0], 0, 0 /* Slow, can fail. */ ); @@ -1724,6 +1730,7 @@ void *sh_emulate_map_dest(struct vcpu *v, unsigned long vaddr, * Cross-page emulated writes are only supported for HVM guests; * PV guests ought to know better. */ + put_page(mfn_to_page(sh_ctxt->mfn[0])); return MAPPING_UNHANDLEABLE; } else @@ -1732,17 +1739,26 @@ void *sh_emulate_map_dest(struct vcpu *v, unsigned long vaddr, sh_ctxt->mfn[1] = emulate_gva_to_mfn( v, (vaddr + bytes - 1) & PAGE_MASK, sh_ctxt); if ( !mfn_valid(sh_ctxt->mfn[1]) ) - return ((mfn_x(sh_ctxt->mfn[1]) == BAD_GVA_TO_GFN) ? - MAPPING_EXCEPTION : - (mfn_x(sh_ctxt->mfn[1]) == READONLY_GFN) ? - MAPPING_SILENT_FAIL : MAPPING_UNHANDLEABLE); + { + put_page(mfn_to_page(sh_ctxt->mfn[0])); + switch ( mfn_x(sh_ctxt->mfn[1]) ) + { + case BAD_GVA_TO_GFN: return MAPPING_EXCEPTION; + case READONLY_GFN: return MAPPING_SILENT_FAIL; + default: return MAPPING_UNHANDLEABLE; + } + } /* Cross-page writes mean probably not a pagetable. */ sh_remove_shadows(d, sh_ctxt->mfn[1], 0, 0 /* Slow, can fail. */ ); map = vmap(sh_ctxt->mfn, 2); if ( !map ) + { + put_page(mfn_to_page(sh_ctxt->mfn[0])); + put_page(mfn_to_page(sh_ctxt->mfn[1])); return MAPPING_UNHANDLEABLE; + } map += (vaddr & ~PAGE_MASK); } @@ -1812,10 +1828,12 @@ void sh_emulate_unmap_dest(struct vcpu *v, void *addr, unsigned int bytes, } paging_mark_dirty(v->domain, sh_ctxt->mfn[0]); + put_page(mfn_to_page(sh_ctxt->mfn[0])); if ( unlikely(mfn_valid(sh_ctxt->mfn[1])) ) { paging_mark_dirty(v->domain, sh_ctxt->mfn[1]); + put_page(mfn_to_page(sh_ctxt->mfn[1])); vunmap((void *)((unsigned long)addr & PAGE_MASK)); } else