aboutsummaryrefslogtreecommitdiffstats
path: root/main/xen/xsa226-2.patch
diff options
context:
space:
mode:
authorDaniel Sabogal <dsabogalcc@gmail.com>2017-08-22 23:11:53 -0400
committerNatanael Copa <ncopa@alpinelinux.org>2017-08-24 22:05:11 +0000
commita9878fc57738d1b777eb47e5fdee082f009e2c38 (patch)
tree4133d5d8ddf5b5c8d87d44fabcb09318d4dd81d6 /main/xen/xsa226-2.patch
parent5bae8450f755f729ff4fe76ab3999a538322cad4 (diff)
downloadaports-a9878fc57738d1b777eb47e5fdee082f009e2c38.tar.bz2
aports-a9878fc57738d1b777eb47e5fdee082f009e2c38.tar.xz
main/xen: security fixes
fixes #7732 CVE-2017-12135 XSA-226 CVE-2017-12137 XSA-227 CVE-2017-12136 XSA-228 CVE-2017-12855 XSA-230
Diffstat (limited to 'main/xen/xsa226-2.patch')
-rw-r--r--main/xen/xsa226-2.patch280
1 files changed, 280 insertions, 0 deletions
diff --git a/main/xen/xsa226-2.patch b/main/xen/xsa226-2.patch
new file mode 100644
index 0000000000..2cf93bdd29
--- /dev/null
+++ b/main/xen/xsa226-2.patch
@@ -0,0 +1,280 @@
+From: Jan Beulich <jbeulich@suse.com>
+Subject: gnttab: fix transitive grant handling
+
+Processing of transitive grants must not use the fast path, or else
+reference counting breaks due to the skipped recursive call to
+__acquire_grant_for_copy() (its __release_grant_for_copy()
+counterpart occurs independent of original pin count). Furthermore
+after re-acquiring temporarily dropped locks we need to verify no grant
+properties changed if the original pin count was non-zero; checking
+just the pin counts is sufficient only for well-behaved guests. As a
+result, __release_grant_for_copy() needs to mirror that new behavior.
+
+Furthermore a __release_grant_for_copy() invocation was missing on the
+retry path of __acquire_grant_for_copy(), and gnttab_set_version() also
+needs to bail out upon encountering a transitive grant.
+
+This is part of CVE-2017-12135 / XSA-226.
+
+Reported-by: Andrew Cooper <andrew.cooper3@citrix.com>
+Signed-off-by: Jan Beulich <jbeulich@suse.com>
+Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
+
+--- a/xen/common/grant_table.c
++++ b/xen/common/grant_table.c
+@@ -2050,13 +2050,8 @@ __release_grant_for_copy(
+ unsigned long r_frame;
+ uint16_t *status;
+ grant_ref_t trans_gref;
+- int released_read;
+- int released_write;
+ struct domain *td;
+
+- released_read = 0;
+- released_write = 0;
+-
+ grant_read_lock(rgt);
+
+ act = active_entry_acquire(rgt, gref);
+@@ -2086,17 +2081,11 @@ __release_grant_for_copy(
+
+ act->pin -= GNTPIN_hstw_inc;
+ if ( !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) )
+- {
+- released_write = 1;
+ gnttab_clear_flag(_GTF_writing, status);
+- }
+ }
+
+ if ( !act->pin )
+- {
+ gnttab_clear_flag(_GTF_reading, status);
+- released_read = 1;
+- }
+
+ active_entry_release(act);
+ grant_read_unlock(rgt);
+@@ -2104,13 +2093,10 @@ __release_grant_for_copy(
+ if ( td != rd )
+ {
+ /*
+- * Recursive calls, but they're bounded (acquire permits only a single
++ * Recursive call, but it is bounded (acquire permits only a single
+ * level of transitivity), so it's okay.
+ */
+- if ( released_write )
+- __release_grant_for_copy(td, trans_gref, 0);
+- else if ( released_read )
+- __release_grant_for_copy(td, trans_gref, 1);
++ __release_grant_for_copy(td, trans_gref, readonly);
+
+ rcu_unlock_domain(td);
+ }
+@@ -2184,8 +2170,108 @@ __acquire_grant_for_copy(
+ act->domid, ldom, act->pin);
+
+ old_pin = act->pin;
+- if ( !act->pin ||
+- (!readonly && !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) )
++ if ( sha2 && (shah->flags & GTF_type_mask) == GTF_transitive )
++ {
++ if ( (!old_pin || (!readonly &&
++ !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)))) &&
++ (rc = _set_status_v2(ldom, readonly, 0, shah, act,
++ status)) != GNTST_okay )
++ goto unlock_out;
++
++ if ( !allow_transitive )
++ PIN_FAIL(unlock_out_clear, GNTST_general_error,
++ "transitive grant when transitivity not allowed\n");
++
++ trans_domid = sha2->transitive.trans_domid;
++ trans_gref = sha2->transitive.gref;
++ barrier(); /* Stop the compiler from re-loading
++ trans_domid from shared memory */
++ if ( trans_domid == rd->domain_id )
++ PIN_FAIL(unlock_out_clear, GNTST_general_error,
++ "transitive grants cannot be self-referential\n");
++
++ /*
++ * We allow the trans_domid == ldom case, which corresponds to a
++ * grant being issued by one domain, sent to another one, and then
++ * transitively granted back to the original domain. Allowing it
++ * is easy, and means that you don't need to go out of your way to
++ * avoid it in the guest.
++ */
++
++ /* We need to leave the rrd locked during the grant copy. */
++ td = rcu_lock_domain_by_id(trans_domid);
++ if ( td == NULL )
++ PIN_FAIL(unlock_out_clear, GNTST_general_error,
++ "transitive grant referenced bad domain %d\n",
++ trans_domid);
++
++ /*
++ * __acquire_grant_for_copy() could take the lock on the
++ * remote table (if rd == td), so we have to drop the lock
++ * here and reacquire.
++ */
++ active_entry_release(act);
++ grant_read_unlock(rgt);
++
++ rc = __acquire_grant_for_copy(td, trans_gref, rd->domain_id,
++ readonly, &grant_frame, page,
++ &trans_page_off, &trans_length, 0);
++
++ grant_read_lock(rgt);
++ act = active_entry_acquire(rgt, gref);
++
++ if ( rc != GNTST_okay )
++ {
++ __fixup_status_for_copy_pin(act, status);
++ rcu_unlock_domain(td);
++ active_entry_release(act);
++ grant_read_unlock(rgt);
++ return rc;
++ }
++
++ /*
++ * We dropped the lock, so we have to check that the grant didn't
++ * change, and that nobody else tried to pin/unpin it. If anything
++ * changed, just give up and tell the caller to retry.
++ */
++ if ( rgt->gt_version != 2 ||
++ act->pin != old_pin ||
++ (old_pin && (act->domid != ldom || act->frame != grant_frame ||
++ act->start != trans_page_off ||
++ act->length != trans_length ||
++ act->trans_domain != td ||
++ act->trans_gref != trans_gref ||
++ !act->is_sub_page)) )
++ {
++ __release_grant_for_copy(td, trans_gref, readonly);
++ __fixup_status_for_copy_pin(act, status);
++ rcu_unlock_domain(td);
++ active_entry_release(act);
++ grant_read_unlock(rgt);
++ put_page(*page);
++ *page = NULL;
++ return ERESTART;
++ }
++
++ if ( !old_pin )
++ {
++ act->domid = ldom;
++ act->start = trans_page_off;
++ act->length = trans_length;
++ act->trans_domain = td;
++ act->trans_gref = trans_gref;
++ act->frame = grant_frame;
++ act->gfn = -1ul;
++ /*
++ * The actual remote remote grant may or may not be a sub-page,
++ * but we always treat it as one because that blocks mappings of
++ * transitive grants.
++ */
++ act->is_sub_page = 1;
++ }
++ }
++ else if ( !old_pin ||
++ (!readonly && !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) )
+ {
+ if ( (rc = _set_status(rgt->gt_version, ldom,
+ readonly, 0, shah, act,
+@@ -2206,79 +2292,6 @@ __acquire_grant_for_copy(
+ trans_page_off = 0;
+ trans_length = PAGE_SIZE;
+ }
+- else if ( (shah->flags & GTF_type_mask) == GTF_transitive )
+- {
+- if ( !allow_transitive )
+- PIN_FAIL(unlock_out_clear, GNTST_general_error,
+- "transitive grant when transitivity not allowed\n");
+-
+- trans_domid = sha2->transitive.trans_domid;
+- trans_gref = sha2->transitive.gref;
+- barrier(); /* Stop the compiler from re-loading
+- trans_domid from shared memory */
+- if ( trans_domid == rd->domain_id )
+- PIN_FAIL(unlock_out_clear, GNTST_general_error,
+- "transitive grants cannot be self-referential\n");
+-
+- /* We allow the trans_domid == ldom case, which
+- corresponds to a grant being issued by one domain, sent
+- to another one, and then transitively granted back to
+- the original domain. Allowing it is easy, and means
+- that you don't need to go out of your way to avoid it
+- in the guest. */
+-
+- /* We need to leave the rrd locked during the grant copy */
+- td = rcu_lock_domain_by_id(trans_domid);
+- if ( td == NULL )
+- PIN_FAIL(unlock_out_clear, GNTST_general_error,
+- "transitive grant referenced bad domain %d\n",
+- trans_domid);
+-
+- /*
+- * __acquire_grant_for_copy() could take the lock on the
+- * remote table (if rd == td), so we have to drop the lock
+- * here and reacquire
+- */
+- active_entry_release(act);
+- grant_read_unlock(rgt);
+-
+- rc = __acquire_grant_for_copy(td, trans_gref, rd->domain_id,
+- readonly, &grant_frame, page,
+- &trans_page_off, &trans_length, 0);
+-
+- grant_read_lock(rgt);
+- act = active_entry_acquire(rgt, gref);
+-
+- if ( rc != GNTST_okay ) {
+- __fixup_status_for_copy_pin(act, status);
+- rcu_unlock_domain(td);
+- active_entry_release(act);
+- grant_read_unlock(rgt);
+- return rc;
+- }
+-
+- /*
+- * We dropped the lock, so we have to check that nobody else tried
+- * to pin (or, for that matter, unpin) the reference in *this*
+- * domain. If they did, just give up and tell the caller to retry.
+- */
+- if ( act->pin != old_pin )
+- {
+- __fixup_status_for_copy_pin(act, status);
+- rcu_unlock_domain(td);
+- active_entry_release(act);
+- grant_read_unlock(rgt);
+- put_page(*page);
+- *page = NULL;
+- return ERESTART;
+- }
+-
+- /* The actual remote remote grant may or may not be a
+- sub-page, but we always treat it as one because that
+- blocks mappings of transitive grants. */
+- is_sub_page = 1;
+- act->gfn = -1ul;
+- }
+ else if ( !(sha2->hdr.flags & GTF_sub_page) )
+ {
+ rc = __get_paged_frame(sha2->full_page.frame, &grant_frame, page, readonly, rd);
+@@ -2710,10 +2723,13 @@ gnttab_set_version(XEN_GUEST_HANDLE_PARA
+ case 2:
+ for ( i = 0; i < GNTTAB_NR_RESERVED_ENTRIES; i++ )
+ {
+- if ( ((shared_entry_v2(gt, i).hdr.flags & GTF_type_mask) ==
+- GTF_permit_access) &&
+- (shared_entry_v2(gt, i).full_page.frame >> 32) )
++ switch ( shared_entry_v2(gt, i).hdr.flags & GTF_type_mask )
+ {
++ case GTF_permit_access:
++ if ( !(shared_entry_v2(gt, i).full_page.frame >> 32) )
++ break;
++ /* fall through */
++ case GTF_transitive:
+ gdprintk(XENLOG_WARNING,
+ "tried to change grant table version to 1 with non-representable entries\n");
+ res = -ERANGE;