summaryrefslogtreecommitdiff
path: root/system/xen/xsa/xsa224-0001-gnttab-Fix-handling-of-dev_bus_addr-during-unmap.patch
diff options
context:
space:
mode:
Diffstat (limited to 'system/xen/xsa/xsa224-0001-gnttab-Fix-handling-of-dev_bus_addr-during-unmap.patch')
-rw-r--r--system/xen/xsa/xsa224-0001-gnttab-Fix-handling-of-dev_bus_addr-during-unmap.patch111
1 files changed, 111 insertions, 0 deletions
diff --git a/system/xen/xsa/xsa224-0001-gnttab-Fix-handling-of-dev_bus_addr-during-unmap.patch b/system/xen/xsa/xsa224-0001-gnttab-Fix-handling-of-dev_bus_addr-during-unmap.patch
new file mode 100644
index 0000000000..6a55f86b07
--- /dev/null
+++ b/system/xen/xsa/xsa224-0001-gnttab-Fix-handling-of-dev_bus_addr-during-unmap.patch
@@ -0,0 +1,111 @@
+From 9808ed0b1ebc3a5d2aa08a9ff91fcf3ecb42bc9f Mon Sep 17 00:00:00 2001
+From: George Dunlap <george.dunlap@citrix.com>
+Date: Thu, 15 Jun 2017 16:24:02 +0100
+Subject: [PATCH 1/4] gnttab: Fix handling of dev_bus_addr during unmap
+
+If a grant has been mapped with the GNTTAB_device_map flag, calling
+grant_unmap_ref() with dev_bus_addr set to zero should cause the
+GNTTAB_device_map part of the mapping to be left alone.
+
+Unfortunately, at the moment, op->dev_bus_addr is implicitly checked
+before clearing the map and adjusting the pin count, but only the bits
+above 12; and it is not checked at all before dropping page
+references. This means a guest can repeatedly make such a call to
+cause the reference count to drop to zero, causing the page to be
+freed and re-used, even though it's still mapped in its pagetables.
+
+To fix this, always check op->dev_bus_addr explicitly for being
+non-zero, as well as op->flag & GNTMAP_device_map, before doing
+operations on the device_map.
+
+While we're here, make the logic a bit cleaner:
+
+* Always initialize op->frame to zero and set it from act->frame, to reduce the
+chance of untrusted input being used
+
+* Explicitly check the full dev_bus_addr against act->frame <<
+ PAGE_SHIFT, rather than ignoring the lower 12 bits
+
+This is part of XSA-224.
+
+Reported-by: Jan Beulich <jbeulich@suse.com>
+Signed-off-by: George Dunlap <george.dunlap@citrix.com>
+Signed-off-by: Jan Beulich <jbeulich@suse.com>
+---
+ xen/common/grant_table.c | 23 +++++++++++------------
+ 1 file changed, 11 insertions(+), 12 deletions(-)
+
+diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c
+index ba10e76..2671761 100644
+--- a/xen/common/grant_table.c
++++ b/xen/common/grant_table.c
+@@ -1085,8 +1085,6 @@ __gnttab_unmap_common(
+ ld = current->domain;
+ lgt = ld->grant_table;
+
+- op->frame = (unsigned long)(op->dev_bus_addr >> PAGE_SHIFT);
+-
+ if ( unlikely(op->handle >= lgt->maptrack_limit) )
+ {
+ gdprintk(XENLOG_INFO, "Bad handle (%d).\n", op->handle);
+@@ -1169,16 +1167,14 @@ __gnttab_unmap_common(
+ goto act_release_out;
+ }
+
+- if ( op->frame == 0 )
+- {
+- op->frame = act->frame;
+- }
+- else
++ op->frame = act->frame;
++
++ if ( op->dev_bus_addr )
+ {
+- if ( unlikely(op->frame != act->frame) )
++ if ( unlikely(op->dev_bus_addr != pfn_to_paddr(act->frame)) )
+ PIN_FAIL(act_release_out, GNTST_general_error,
+- "Bad frame number doesn't match gntref. (%lx != %lx)\n",
+- op->frame, act->frame);
++ "Bus address doesn't match gntref (%"PRIx64" != %"PRIpaddr")\n",
++ op->dev_bus_addr, pfn_to_paddr(act->frame));
+
+ map->flags &= ~GNTMAP_device_map;
+ }
+@@ -1271,7 +1267,8 @@ __gnttab_unmap_common_complete(struct gnttab_unmap_common *op)
+ else
+ status = &status_entry(rgt, op->ref);
+
+- if ( unlikely(op->frame != act->frame) )
++ if ( op->dev_bus_addr &&
++ unlikely(op->dev_bus_addr != pfn_to_paddr(act->frame)) )
+ {
+ /*
+ * Suggests that __gntab_unmap_common failed early and so
+@@ -1282,7 +1279,7 @@ __gnttab_unmap_common_complete(struct gnttab_unmap_common *op)
+
+ pg = mfn_to_page(op->frame);
+
+- if ( op->flags & GNTMAP_device_map )
++ if ( op->dev_bus_addr && (op->flags & GNTMAP_device_map) )
+ {
+ if ( !is_iomem_page(act->frame) )
+ {
+@@ -1353,6 +1350,7 @@ __gnttab_unmap_grant_ref(
+ /* Intialise these in case common contains old state */
+ common->new_addr = 0;
+ common->rd = NULL;
++ common->frame = 0;
+
+ __gnttab_unmap_common(common);
+ op->status = common->status;
+@@ -1417,6 +1415,7 @@ __gnttab_unmap_and_replace(
+ /* Intialise these in case common contains old state */
+ common->dev_bus_addr = 0;
+ common->rd = NULL;
++ common->frame = 0;
+
+ __gnttab_unmap_common(common);
+ op->status = common->status;
+--
+2.1.4
+