]> xenbits.xensource.com Git - xen.git/commitdiff
multicall: deal with early exit conditions
authorJan Beulich <jbeulich@suse.com>
Tue, 2 May 2017 13:01:12 +0000 (15:01 +0200)
committerJan Beulich <jbeulich@suse.com>
Tue, 2 May 2017 13:01:12 +0000 (15:01 +0200)
In particular changes to guest privilege level require the multicall
sequence to be aborted, as hypercalls are permitted from kernel mode
only. While likely not very useful in a multicall, also properly handle
the return value in the HYPERVISOR_iret case (which should be the guest
specified value).

This is XSA-213.

Reported-by: Jann Horn <jannh@google.com>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Julien Grall <julien.grall@arm.com>
master commit: 22c096c99d8c05833c3c19870e36efb2dd4e8013
master date: 2017-05-02 14:45:02 +0200

xen/arch/arm/traps.c
xen/common/multicall.c
xen/include/asm-arm/multicall.h
xen/include/asm-x86/multicall.h

index 5499493ab5c57c192823ab7d7c5b1f948999f79d..551409a1cb4523345dedf3ae3e9f0e94cef146c4 100644 (file)
@@ -1504,30 +1504,33 @@ static bool_t check_multicall_32bit_clean(struct multicall_entry *multi)
     return true;
 }
 
-void do_multicall_call(struct multicall_entry *multi)
+enum mc_disposition do_multicall_call(struct multicall_entry *multi)
 {
     arm_hypercall_fn_t call = NULL;
 
     if ( multi->op >= ARRAY_SIZE(arm_hypercall_table) )
     {
         multi->result = -ENOSYS;
-        return;
+        return mc_continue;
     }
 
     call = arm_hypercall_table[multi->op].fn;
     if ( call == NULL )
     {
         multi->result = -ENOSYS;
-        return;
+        return mc_continue;
     }
 
     if ( is_32bit_domain(current->domain) &&
          !check_multicall_32bit_clean(multi) )
-        return;
+        return mc_continue;
 
     multi->result = call(multi->args[0], multi->args[1],
                          multi->args[2], multi->args[3],
                          multi->args[4]);
+
+    return likely(!psr_mode_is_user(guest_cpu_user_regs()))
+           ? mc_continue : mc_preempt;
 }
 
 /*
index fa9d91059455013bda37b12caa890aaa0d8a4b81..da13573600c8d97a51d4bc81f713603851a57d59 100644 (file)
@@ -40,6 +40,7 @@ do_multicall(
     struct mc_state *mcs = &current->mc_state;
     uint32_t         i;
     int              rc = 0;
+    enum mc_disposition disp = mc_continue;
 
     if ( unlikely(__test_and_set_bit(_MCSF_in_multicall, &mcs->flags)) )
     {
@@ -50,7 +51,7 @@ do_multicall(
     if ( unlikely(!guest_handle_okay(call_list, nr_calls)) )
         rc = -EFAULT;
 
-    for ( i = 0; !rc && i < nr_calls; i++ )
+    for ( i = 0; !rc && disp == mc_continue && i < nr_calls; i++ )
     {
         if ( i && hypercall_preempt_check() )
             goto preempted;
@@ -63,7 +64,7 @@ do_multicall(
 
         trace_multicall_call(&mcs->call);
 
-        do_multicall_call(&mcs->call);
+        disp = do_multicall_call(&mcs->call);
 
 #ifndef NDEBUG
         {
@@ -77,7 +78,14 @@ do_multicall(
         }
 #endif
 
-        if ( unlikely(__copy_field_to_guest(call_list, &mcs->call, result)) )
+        if ( unlikely(disp == mc_exit) )
+        {
+            if ( __copy_field_to_guest(call_list, &mcs->call, result) )
+                /* nothing, best effort only */;
+            rc = mcs->call.result;
+        }
+        else if ( unlikely(__copy_field_to_guest(call_list, &mcs->call,
+                                                 result)) )
             rc = -EFAULT;
         else if ( test_bit(_MCSF_call_preempted, &mcs->flags) )
         {
@@ -93,6 +101,9 @@ do_multicall(
             guest_handle_add_offset(call_list, 1);
     }
 
+    if ( unlikely(disp == mc_preempt) && i < nr_calls )
+        goto preempted;
+
     perfc_incr(calls_to_multicall);
     perfc_add(calls_from_multicall, i);
     mcs->flags = 0;
index b95926274fe18ce16fe8a1b0cd39adae5f185119..ee3b3459036590396675fdbcb203ae815b5b65cd 100644 (file)
@@ -1,7 +1,11 @@
 #ifndef __ASM_ARM_MULTICALL_H__
 #define __ASM_ARM_MULTICALL_H__
 
-extern void do_multicall_call(struct multicall_entry *call);
+extern enum mc_disposition {
+    mc_continue,
+    mc_exit,
+    mc_preempt,
+} do_multicall_call(struct multicall_entry *call);
 
 #endif /* __ASM_ARM_MULTICALL_H__ */
 /*
index fcd0ea518c49881e5da5f6d646192cdc522436ac..3cb0b6d9a0fa82ebd57c68c42c9c408cb3a626a9 100644 (file)
@@ -7,8 +7,21 @@
 
 #include <xen/errno.h>
 
+enum mc_disposition {
+    mc_continue,
+    mc_exit,
+    mc_preempt,
+};
+
+#define multicall_ret(call)                                  \
+    (unlikely((call)->op == __HYPERVISOR_iret)               \
+     ? mc_exit                                               \
+       : likely(guest_kernel_mode(current,                   \
+                                  guest_cpu_user_regs()))    \
+         ? mc_continue : mc_preempt)
+
 #define do_multicall_call(_call)                             \
-    do {                                                     \
+    ({                                                       \
         __asm__ __volatile__ (                               \
             "    movq  %c1(%0),%%rax; "                      \
             "    leaq  hypercall_table(%%rip),%%rdi; "       \
               /* all the caller-saves registers */           \
             : "rax", "rcx", "rdx", "rsi", "rdi",             \
               "r8",  "r9",  "r10", "r11" );                  \
-    } while ( 0 )
+        multicall_ret(_call);                                \
+    })
 
 #define compat_multicall_call(_call)                         \
+    ({                                                       \
         __asm__ __volatile__ (                               \
             "    movl  %c1(%0),%%eax; "                      \
             "    leaq  compat_hypercall_table(%%rip),%%rdi; "\
@@ -67,6 +82,8 @@
               "i" (-ENOSYS)                                  \
               /* all the caller-saves registers */           \
             : "rax", "rcx", "rdx", "rsi", "rdi",             \
-              "r8",  "r9",  "r10", "r11" )                   \
+              "r8",  "r9",  "r10", "r11" );                  \
+        multicall_ret(_call);                                \
+    })
 
 #endif /* __ASM_X86_MULTICALL_H__ */