diff options
Diffstat (limited to 'system/xen/xsa/xsa255-2.patch')
-rw-r--r-- | system/xen/xsa/xsa255-2.patch | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/system/xen/xsa/xsa255-2.patch b/system/xen/xsa/xsa255-2.patch new file mode 100644 index 0000000000000..402b6efe98c26 --- /dev/null +++ b/system/xen/xsa/xsa255-2.patch @@ -0,0 +1,167 @@ +From: Jan Beulich <jbeulich@suse.com> +Subject: gnttab: don't blindly free status pages upon version change + +There may still be active mappings, which would trigger the respective +BUG_ON(). Split the loop into one dealing with the page attributes and +the second (when the first fully passed) freeing the pages. Return an +error if any pages still have pending references. + +This is part of XSA-255. + +Signed-off-by: Jan Beulich <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> +Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com> +--- +v4: Add gprintk(XENLOG_ERR, ...) to domain_crash() invocations. +v3: Call guest_physmap_remove_page() from gnttab_map_frame(), making the + code unconditional at the same time. Re-base over changes to first + patch. +v2: Also deal with translated guests. + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -1636,23 +1636,74 @@ status_alloc_failed: + return -ENOMEM; + } + +-static void ++static int + gnttab_unpopulate_status_frames(struct domain *d, struct grant_table *gt) + { +- int i; ++ unsigned int i; + + for ( i = 0; i < nr_status_frames(gt); i++ ) + { + struct page_info *pg = virt_to_page(gt->status[i]); ++ gfn_t gfn = gnttab_get_frame_gfn(gt, true, i); ++ ++ /* ++ * For translated domains, recovering from failure after partial ++ * changes were made is more complicated than it seems worth ++ * implementing at this time. Hence respective error paths below ++ * crash the domain in such a case. ++ */ ++ if ( paging_mode_translate(d) ) ++ { ++ int rc = gfn_eq(gfn, INVALID_GFN) ++ ? 0 ++ : guest_physmap_remove_page(d, gfn, ++ _mfn(page_to_mfn(pg)), 0); ++ ++ if ( rc ) ++ { ++ gprintk(XENLOG_ERR, ++ "Could not remove status frame %u (GFN %#lx) from P2M\n", ++ i, gfn_x(gfn)); ++ domain_crash(d); ++ return rc; ++ } ++ gnttab_set_frame_gfn(gt, true, i, INVALID_GFN); ++ } + + BUG_ON(page_get_owner(pg) != d); + if ( test_and_clear_bit(_PGC_allocated, &pg->count_info) ) + put_page(pg); +- BUG_ON(pg->count_info & ~PGC_xen_heap); ++ ++ if ( pg->count_info & ~PGC_xen_heap ) ++ { ++ if ( paging_mode_translate(d) ) ++ { ++ gprintk(XENLOG_ERR, ++ "Wrong page state %#lx of status frame %u (GFN %#lx)\n", ++ pg->count_info, i, gfn_x(gfn)); ++ domain_crash(d); ++ } ++ else ++ { ++ if ( get_page(pg, d) ) ++ set_bit(_PGC_allocated, &pg->count_info); ++ while ( i-- ) ++ gnttab_create_status_page(d, gt, i); ++ } ++ return -EBUSY; ++ } ++ ++ page_set_owner(pg, NULL); ++ } ++ ++ for ( i = 0; i < nr_status_frames(gt); i++ ) ++ { + free_xenheap_page(gt->status[i]); + gt->status[i] = NULL; + } + gt->nr_status_frames = 0; ++ ++ return 0; + } + + /* +@@ -2962,8 +3013,9 @@ gnttab_set_version(XEN_GUEST_HANDLE_PARA + break; + } + +- if ( op.version < 2 && gt->gt_version == 2 ) +- gnttab_unpopulate_status_frames(currd, gt); ++ if ( op.version < 2 && gt->gt_version == 2 && ++ (res = gnttab_unpopulate_status_frames(currd, gt)) != 0 ) ++ goto out_unlock; + + /* Make sure there's no crud left over from the old version. */ + for ( i = 0; i < nr_grant_frames(gt); i++ ) +@@ -3803,6 +3855,11 @@ int gnttab_map_frame(struct domain *d, u + rc = -EINVAL; + } + ++ if ( !rc && paging_mode_translate(d) && ++ !gfn_eq(gnttab_get_frame_gfn(gt, status, idx), INVALID_GFN) ) ++ rc = guest_physmap_remove_page(d, gnttab_get_frame_gfn(gt, status, idx), ++ *mfn, 0); ++ + if ( !rc ) + gnttab_set_frame_gfn(gt, status, idx, gfn); + +--- a/xen/include/asm-arm/grant_table.h ++++ b/xen/include/asm-arm/grant_table.h +@@ -73,6 +73,11 @@ static inline unsigned int gnttab_dom0_m + (gfn); \ + } while ( 0 ) + ++#define gnttab_get_frame_gfn(gt, st, idx) ({ \ ++ _gfn((st) ? gnttab_status_gmfn(NULL, gt, idx) \ ++ : gnttab_shared_gmfn(NULL, gt, idx)); \ ++}) ++ + #define gnttab_create_shared_page(d, t, i) \ + do { \ + share_xen_page_with_guest( \ +--- a/xen/include/asm-x86/grant_table.h ++++ b/xen/include/asm-x86/grant_table.h +@@ -47,6 +47,12 @@ static inline unsigned int gnttab_dom0_m + #define gnttab_init_arch(gt) 0 + #define gnttab_destroy_arch(gt) do {} while ( 0 ) + #define gnttab_set_frame_gfn(gt, st, idx, gfn) do {} while ( 0 ) ++#define gnttab_get_frame_gfn(gt, st, idx) ({ \ ++ unsigned long mfn_ = (st) ? gnttab_status_mfn(gt, idx) \ ++ : gnttab_shared_mfn(gt, idx); \ ++ unsigned long gpfn_ = get_gpfn_from_mfn(mfn_); \ ++ VALID_M2P(gpfn_) ? _gfn(gpfn_) : INVALID_GFN; \ ++}) + + #define gnttab_create_shared_page(d, t, i) \ + do { \ +@@ -63,11 +69,11 @@ static inline unsigned int gnttab_dom0_m + } while ( 0 ) + + +-#define gnttab_shared_mfn(d, t, i) \ ++#define gnttab_shared_mfn(t, i) \ + ((virt_to_maddr((t)->shared_raw[i]) >> PAGE_SHIFT)) + + #define gnttab_shared_gmfn(d, t, i) \ +- (mfn_to_gmfn(d, gnttab_shared_mfn(d, t, i))) ++ (mfn_to_gmfn(d, gnttab_shared_mfn(t, i))) + + + #define gnttab_status_mfn(t, i) \ |