bb4388c
From: Jan Beulich <jbeulich@suse.com>
bb4388c
Subject: gnttab: don't use possibly unbounded tail calls
bb4388c
bb4388c
There is no guarantee that the compiler would actually translate them
bb4388c
to branches instead of calls, so only ones with a known recursion limit
bb4388c
are okay:
bb4388c
- __release_grant_for_copy() can call itself only once, as
bb4388c
  __acquire_grant_for_copy() won't permit use of multi-level transitive
bb4388c
  grants,
bb4388c
- __acquire_grant_for_copy() is fine to call itself with the last
bb4388c
  argument false, as that prevents further recursion,
bb4388c
- __acquire_grant_for_copy() must not call itself to recover from an
bb4388c
  observed change to the active entry's pin count
bb4388c
bb4388c
This is part of CVE-2017-12135 / XSA-226.
bb4388c
bb4388c
Signed-off-by: Jan Beulich <jbeulich@suse.com>
bb4388c
ba5e947
--- a/xen/common/compat/grant_table.c
ba5e947
+++ b/xen/common/compat/grant_table.c
ba5e947
@@ -258,9 +258,9 @@ int compat_grant_table_op(unsigned int cmd,
ba5e947
                 rc = gnttab_copy(guest_handle_cast(nat.uop, gnttab_copy_t), n);
ba5e947
             if ( rc > 0 )
ba5e947
             {
ba5e947
-                ASSERT(rc < n);
ba5e947
-                i -= n - rc;
ba5e947
-                n = rc;
ba5e947
+                ASSERT(rc <= n);
ba5e947
+                i -= rc;
ba5e947
+                n -= rc;
ba5e947
             }
ba5e947
             if ( rc >= 0 )
ba5e947
             {
bb4388c
--- a/xen/common/grant_table.c
bb4388c
+++ b/xen/common/grant_table.c
bb4388c
@@ -2103,8 +2103,10 @@ __release_grant_for_copy(
bb4388c
 
bb4388c
     if ( td != rd )
bb4388c
     {
bb4388c
-        /* Recursive calls, but they're tail calls, so it's
bb4388c
-           okay. */
bb4388c
+        /*
bb4388c
+         * Recursive calls, but they're bounded (acquire permits only a single
bb4388c
+         * level of transitivity), so it's okay.
bb4388c
+         */
bb4388c
         if ( released_write )
bb4388c
             __release_grant_for_copy(td, trans_gref, 0);
bb4388c
         else if ( released_read )
bb4388c
@@ -2255,10 +2257,11 @@ __acquire_grant_for_copy(
bb4388c
                 return rc;
bb4388c
             }
bb4388c
 
bb4388c
-            /* We dropped the lock, so we have to check that nobody
bb4388c
-               else tried to pin (or, for that matter, unpin) the
bb4388c
-               reference in *this* domain.  If they did, just give up
bb4388c
-               and try again. */
bb4388c
+            /*
bb4388c
+             * We dropped the lock, so we have to check that nobody else tried
bb4388c
+             * to pin (or, for that matter, unpin) the reference in *this*
bb4388c
+             * domain.  If they did, just give up and tell the caller to retry.
bb4388c
+             */
bb4388c
             if ( act->pin != old_pin )
bb4388c
             {
bb4388c
                 __fixup_status_for_copy_pin(act, status);
bb4388c
@@ -2266,9 +2269,8 @@ __acquire_grant_for_copy(
bb4388c
                 active_entry_release(act);
bb4388c
                 grant_read_unlock(rgt);
bb4388c
                 put_page(*page);
bb4388c
-                return __acquire_grant_for_copy(rd, gref, ldom, readonly,
bb4388c
-                                                frame, page, page_off, length,
bb4388c
-                                                allow_transitive);
bb4388c
+                *page = NULL;
bb4388c
+                return ERESTART;
bb4388c
             }
bb4388c
 
bb4388c
             /* The actual remote remote grant may or may not be a
bb4388c
@@ -2574,7 +2576,7 @@ static int gnttab_copy_one(const struct
bb4388c
     {
bb4388c
         gnttab_copy_release_buf(src);
bb4388c
         rc = gnttab_copy_claim_buf(op, &op->source, src, GNTCOPY_source_gref);
bb4388c
-        if ( rc < 0 )
bb4388c
+        if ( rc )
bb4388c
             goto out;
bb4388c
     }
bb4388c
 
bb4388c
@@ -2584,7 +2586,7 @@ static int gnttab_copy_one(const struct
bb4388c
     {
bb4388c
         gnttab_copy_release_buf(dest);
bb4388c
         rc = gnttab_copy_claim_buf(op, &op->dest, dest, GNTCOPY_dest_gref);
bb4388c
-        if ( rc < 0 )
bb4388c
+        if ( rc )
bb4388c
             goto out;
bb4388c
     }
bb4388c
 
bb4388c
@@ -2593,6 +2595,14 @@ static int gnttab_copy_one(const struct
bb4388c
     return rc;
bb4388c
 }
bb4388c
 
bb4388c
+/*
bb4388c
+ * gnttab_copy(), other than the various other helpers of
bb4388c
+ * do_grant_table_op(), returns (besides possible error indicators)
bb4388c
+ * "count - i" rather than "i" to ensure that even if no progress
bb4388c
+ * was made at all (perhaps due to gnttab_copy_one() returning a
bb4388c
+ * positive value) a non-zero value is being handed back (zero needs
bb4388c
+ * to be avoided, as that means "success, all done").
bb4388c
+ */
bb4388c
 static long gnttab_copy(
bb4388c
     XEN_GUEST_HANDLE_PARAM(gnttab_copy_t) uop, unsigned int count)
bb4388c
 {
bb4388c
@@ -2606,7 +2616,7 @@ static long gnttab_copy(
bb4388c
     {
bb4388c
         if ( i && hypercall_preempt_check() )
bb4388c
         {
bb4388c
-            rc = i;
bb4388c
+            rc = count - i;
bb4388c
             break;
bb4388c
         }
bb4388c
 
bb4388c
@@ -2616,13 +2626,20 @@ static long gnttab_copy(
bb4388c
             break;
bb4388c
         }
bb4388c
 
bb4388c
-        op.status = gnttab_copy_one(&op, &dest, &src;;
bb4388c
-        if ( op.status != GNTST_okay )
bb4388c
+        rc = gnttab_copy_one(&op, &dest, &src;;
bb4388c
+        if ( rc > 0 )
bb4388c
+        {
bb4388c
+            rc = count - i;
bb4388c
+            break;
bb4388c
+        }
bb4388c
+        if ( rc != GNTST_okay )
bb4388c
         {
bb4388c
             gnttab_copy_release_buf(&src;;
bb4388c
             gnttab_copy_release_buf(&dest);
bb4388c
         }
bb4388c
 
bb4388c
+        op.status = rc;
bb4388c
+        rc = 0;
bb4388c
         if ( unlikely(__copy_field_to_guest(uop, &op, status)) )
bb4388c
         {
bb4388c
             rc = -EFAULT;
bb4388c
@@ -3160,6 +3177,7 @@ do_grant_table_op(
bb4388c
         rc = gnttab_copy(copy, count);
bb4388c
         if ( rc > 0 )
bb4388c
         {
bb4388c
+            rc = count - rc;
bb4388c
             guest_handle_add_offset(copy, rc);
bb4388c
             uop = guest_handle_cast(copy, void);
bb4388c
         }