aboutsummaryrefslogtreecommitdiff
path: root/system/xen/xsa/xsa255-2.patch
diff options
context:
space:
mode:
Diffstat (limited to 'system/xen/xsa/xsa255-2.patch')
-rw-r--r--system/xen/xsa/xsa255-2.patch167
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) \