x86: avoid calling {svm,vmx}_do_resume()

These functions follow the following path: hvm_do_resume() ->
handle_hvm_io_completion() -> hvm_wait_for_io() ->
wait_on_xen_event_channel() -> do_softirq() -> schedule() ->
sched_context_switch() -> continue_running() and hence may
recursively invoke themselves. If this ends up happening a couple of
times, a stack overflow would result.

Prevent this by also resetting the stack at the
->arch.ctxt_switch->tail() invocations (in both places for consistency)
and thus jumping to the functions instead of calling them.

This is XSA-348 / CVE-2020-29566.

Reported-by: Julien Grall <jgrall@amazon.com>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Juergen Gross <jgross@suse.com>

--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -70,12 +70,9 @@ void (*dead_idle) (void) __read_mostly =
 static void paravirt_ctxt_switch_from(struct vcpu *v);
 static void paravirt_ctxt_switch_to(struct vcpu *v);
 
-static void continue_idle_domain(struct vcpu *v)
-{
-    reset_stack_and_jump(idle_loop);
-}
+void noreturn ret_from_intr(void);
 
-static void continue_nonidle_domain(struct vcpu *v)
+static void noreturn continue_nonidle_domain(void)
 {
     reset_stack_and_jump(ret_from_intr);
 }
@@ -124,7 +121,7 @@ static void play_dead(void)
     (*dead_idle)();
 }
 
-void idle_loop(void)
+static void noreturn idle_loop(void)
 {
     for ( ; ; )
     {
@@ -385,7 +382,7 @@ int vcpu_initialise(struct vcpu *v)
 
         if ( is_idle_domain(d) )
         {
-            v->arch.schedule_tail = continue_idle_domain;
+            v->arch.schedule_tail = idle_loop;
             v->arch.cr3           = __pa(idle_pg_table);
         }
 
@@ -1612,14 +1609,12 @@ void context_switch(struct vcpu *prev, s
     if (prev != next)
         update_runstate_area(next);
 
-    schedule_tail(next);
-    BUG();
+    reset_stack_and_jump_ind(next->arch.schedule_tail);
 }
 
 void continue_running(struct vcpu *same)
 {
-    schedule_tail(same);
-    BUG();
+    reset_stack_and_jump_ind(same->arch.schedule_tail);
 }
 
 int __sync_lazy_execstate(void)
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -55,6 +55,8 @@
 #include <asm/hap.h>
 #include <asm/debugger.h>       
 
+void noreturn svm_asm_do_resume(void);
+
 u32 svm_feature_flags;
 
 /* Indicates whether guests may use EFER.LMSLE. */
@@ -689,8 +691,9 @@ static void svm_ctxt_switch_to(struct vc
         wrmsrl(MSR_TSC_AUX, hvm_msr_tsc_aux(v));
 }
 
-static void svm_do_resume(struct vcpu *v) 
+static void noreturn svm_do_resume(void)
 {
+    struct vcpu *v = current;
     bool_t debug_state = v->domain->debugger_attached;
 
     if ( !(v->arch.flags & TF_launched) )
--- a/xen/arch/x86/hvm/vmx/vmcs.c
+++ b/xen/arch/x86/hvm/vmx/vmcs.c
@@ -1137,8 +1137,9 @@ static void wbinvd_ipi(void *info)
     wbinvd();
 }
 
-void vmx_do_resume(struct vcpu *v)
+void vmx_do_resume(void)
 {
+    struct vcpu *v = current;
     bool_t debug_state;
 
     if ( v->arch.hvm_vmx.active_cpu == smp_processor_id() )
--- a/xen/include/asm-x86/current.h
+++ b/xen/include/asm-x86/current.h
@@ -79,12 +79,21 @@ static inline struct cpu_info *get_cpu_i
 #define get_stack_bottom()                      \
     ((unsigned long)&get_cpu_info()->guest_cpu_user_regs.es)
 
-#define reset_stack_and_jump(__fn)              \
-    __asm__ __volatile__ (                      \
-        "mov %0,%%"__OP"sp; jmp "STR(__fn)      \
-        : : "r" (guest_cpu_user_regs()) : "memory" )
+#define switch_stack_and_jump(fn, instr, constr)                        \
+    ({                                                                  \
+        __asm__ __volatile__ (                                          \
+            "mov %0,%%"__OP"sp;"                                        \
+            instr "1"                                                   \
+            : : "r" (guest_cpu_user_regs()), constr (fn) : "memory" );  \
+        unreachable();                                                  \
+    })
 
-#define schedule_tail(vcpu) (((vcpu)->arch.schedule_tail)(vcpu))
+#define reset_stack_and_jump(fn)                                        \
+    switch_stack_and_jump(fn, "jmp %c", "i")
+
+/* The constraint may only specify non-call-clobbered registers. */
+#define reset_stack_and_jump_ind(fn)                                    \
+    switch_stack_and_jump(fn, "INDIRECT_JMP %", "b")
 
 /*
  * Which VCPU's state is currently running on each CPU?
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -392,7 +392,7 @@ struct arch_vcpu
 
     unsigned long      flags; /* TF_ */
 
-    void (*schedule_tail) (struct vcpu *);
+    void noreturn (*schedule_tail) (void);
 
     void (*ctxt_switch_from) (struct vcpu *);
     void (*ctxt_switch_to) (struct vcpu *);
--- a/xen/include/asm-x86/hvm/vmx/vmx.h
+++ b/xen/include/asm-x86/hvm/vmx/vmx.h
@@ -58,7 +58,7 @@ typedef union {
 void vmx_asm_vmexit_handler(struct cpu_user_regs);
 void vmx_asm_do_vmentry(void);
 void vmx_intr_assist(void);
-void vmx_do_resume(struct vcpu *);
+void noreturn vmx_do_resume(void);
 void vmx_vlapic_msr_changed(struct vcpu *v);
 void vmx_realmode(struct cpu_user_regs *regs);
 void vmx_update_debug_state(struct vcpu *v);
--- a/xen/include/xen/compiler.h
+++ b/xen/include/xen/compiler.h
@@ -16,6 +16,12 @@
 
 #define noreturn      __attribute__((noreturn))
 
+#if (!defined(__clang__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 5))
+#define unreachable() do {} while (1)
+#else
+#define unreachable() __builtin_unreachable()
+#endif
+
 #define __attribute_pure__  __attribute__((pure))
 #define __attribute_const__ __attribute__((__const__))
 
