aboutsummaryrefslogtreecommitdiffstats
path: root/main/xen/xsa255-4.9-2.patch
diff options
context:
space:
mode:
Diffstat (limited to 'main/xen/xsa255-4.9-2.patch')
-rw-r--r--main/xen/xsa255-4.9-2.patch186
1 files changed, 186 insertions, 0 deletions
diff --git a/main/xen/xsa255-4.9-2.patch b/main/xen/xsa255-4.9-2.patch
new file mode 100644
index 0000000000..ce225bde28
--- /dev/null
+++ b/main/xen/xsa255-4.9-2.patch
@@ -0,0 +1,186 @@
+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>
+
+--- a/xen/arch/arm/mm.c
++++ b/xen/arch/arm/mm.c
+@@ -1180,12 +1180,22 @@ int xenmem_add_to_physmap_one(
+ mfn = mfn_x(INVALID_MFN);
+ }
+
++ if ( mfn != mfn_x(INVALID_MFN) &&
++ !gfn_eq(gnttab_get_frame_gfn(d, status, idx), INVALID_GFN) )
++ {
++ rc = guest_physmap_remove_page(d,
++ gnttab_get_frame_gfn(d, status, idx),
++ _mfn(mfn), 0);
++ if ( rc )
++ {
++ grant_write_unlock(d->grant_table);
++ return rc;
++ }
++ }
++
+ if ( mfn != mfn_x(INVALID_MFN) )
+ {
+- if ( status )
+- d->arch.grant_status_gfn[idx] = gfn;
+- else
+- d->arch.grant_shared_gfn[idx] = gfn;
++ gnttab_set_frame_gfn(d, status, idx, gfn);
+
+ t = p2m_ram_rw;
+ }
+--- a/xen/common/grant_table.c
++++ b/xen/common/grant_table.c
+@@ -1516,23 +1516,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(d, 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(d, 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;
+ }
+
+ /*
+@@ -2774,8 +2825,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++ )
+--- a/xen/include/asm-arm/grant_table.h
++++ b/xen/include/asm-arm/grant_table.h
+@@ -20,6 +20,17 @@ static inline int replace_grant_supporte
+ return 1;
+ }
+
++#define gnttab_set_frame_gfn(d, st, idx, gfn) \
++ do { \
++ ((st) ? (d)->arch.grant_status_gfn \
++ : (d)->arch.grant_shared_gfn)[idx] = (gfn); \
++ } while ( 0 )
++
++#define gnttab_get_frame_gfn(d, st, idx) ({ \
++ _gfn((st) ? gnttab_status_gmfn(d, (d)->grant_table, idx) \
++ : gnttab_shared_gmfn(d, (d)->grant_table, 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
+@@ -18,6 +18,14 @@ int create_grant_host_mapping(uint64_t a
+ int replace_grant_host_mapping(
+ uint64_t addr, unsigned long frame, uint64_t new_addr, unsigned int flags);
+
++#define gnttab_set_frame_gfn(d, st, idx, gfn) do {} while ( 0 )
++#define gnttab_get_frame_gfn(d, st, idx) ({ \
++ unsigned long mfn_ = (st) ? gnttab_status_mfn((d)->grant_table, idx) \
++ : gnttab_shared_mfn((d)->grant_table, 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 { \
+ share_xen_page_with_guest( \
+@@ -33,11 +41,11 @@ int replace_grant_host_mapping(
+ } 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) \