[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH RFC v3 3/6] HVM x86 deprivileged mode: Trap handlers for deprivileged mode



Added trap handlers to catch exceptions such as a page fault, general
protection fault, etc. These handlers will crash the domain as such exceptions
would indicate that either there is a bug in deprivileged mode or it has been
compromised by an attacker.

On calling a domain_crash() whilst in deprivileged mode, we need to restore
the host's context so that we do not have guest-defined registers and values
in use after this point due to lazy loading of these values in the SVM and VMX
implementations.

Signed-off-by: Ben Catterall <Ben.Catterall@xxxxxxxxxx>

Changed since v1
----------------
 * Changed to domain_crash(), domain_crash_synchronous was used previously.
 * Updated to perform a HVM context switch on crashing a domain
 * Updated hvm_deprivileged_check_trap() to return a testable error
   code and return based on this.

Changed since v2
----------------
 * Coding style: Added space after if, for, etc.
 * hvm_deprivileged_user_mode() now returns a value to indicate success or
   failure.
---
 xen/arch/x86/hvm/deprivileged.c    | 70 +++++++++++++++++++++++++++++++++++++-
 xen/arch/x86/traps.c               | 55 ++++++++++++++++++++++++++++++
 xen/include/xen/hvm/deprivileged.h | 25 +++++++++++++-
 3 files changed, 148 insertions(+), 2 deletions(-)

diff --git a/xen/arch/x86/hvm/deprivileged.c b/xen/arch/x86/hvm/deprivileged.c
index 5574c50..68c40ad 100644
--- a/xen/arch/x86/hvm/deprivileged.c
+++ b/xen/arch/x86/hvm/deprivileged.c
@@ -560,7 +560,7 @@ void hvm_deprivileged_destroy_vcpu(struct vcpu *vcpu)
  * This method is then jumped into to restore execution context after
  * exiting user mode.
  */
-void hvm_deprivileged_user_mode(void)
+int hvm_deprivileged_user_mode(void)
 {
     struct vcpu *vcpu = get_current();
 
@@ -576,6 +576,20 @@ void hvm_deprivileged_user_mode(void)
 
     vcpu->arch.hvm_vcpu.depriv_user_mode = 0;
     vcpu->arch.hvm_vcpu.depriv_rsp = 0;
+
+    /*
+     * If we need to crash the domain at this point. We will return up the call
+     * stack, undoing any allocations and then the event testers in the exit
+     * assembly stubs will test for the SOFTIRQ_TIMER event generated by a
+     * domain_crash and will crash the domain for us.
+     */
+    if ( vcpu->arch.hvm_vcpu.depriv_destroy )
+    {
+        domain_crash(vcpu->domain);
+        return 1;
+    }
+
+    return 0;
 }
 
 /*
@@ -639,3 +653,57 @@ void hvm_deprivileged_finish_user_mode(void)
 
     hvm_deprivileged_finish_user_mode_asm();
 }
+
+/* Check if we are in deprivileged mode */
+int is_hvm_deprivileged_vcpu(void)
+{
+    struct vcpu *v = get_current();
+
+    if ( is_hvm_vcpu(v) && (v->arch.hvm_vcpu.depriv_user_mode) )
+        return 1;
+
+    return 0;
+}
+
+/*
+ * Crash the domain. This should not be called if there are any memory
+ * allocations which will be freed by code following its invocation in the
+ * current execution context (current stack). This is because it causes a
+ * permanent 'context switch' and the current stack will be cloberred so
+ * any allocations made which are not freed by other paths will leak.
+ * This function should only be used after deprivileged mode has been
+ * successfully switched into, otherwise, the normal domain_crash function
+ * should be used.
+ *
+ * The domain which is crashed is that of the current vcpu.
+ *
+ * To crash the domain, we need to return to our privileged stack as we may 
have
+ * memory allocations which need to be cleaned up. Then, after we have returned
+ * to this stack, we can then crash the domain. We set a flag which we check
+ * when returning.
+ */
+void hvm_deprivileged_crash_domain(const char *reason)
+{
+    struct vcpu *vcpu = get_current();
+
+    vcpu->arch.hvm_vcpu.depriv_destroy = 1;
+
+    printk(XENLOG_ERR "HVM Deprivileged Mode: Crashing domain. Reason: %s\n",
+           reason);
+
+    /*
+     * Restore the processor's state. We need to do the privileged return
+     * path to undo any allocations that got us to this state
+     */
+    hvm_deprivileged_finish_user_mode();
+    /* DOES NOT RETURN */
+}
+
+/* Handle a trap event */
+int hvm_deprivileged_check_trap(const char* func_name)
+{
+    if ( is_hvm_deprivileged_vcpu() )
+        hvm_deprivileged_crash_domain(func_name);
+
+    return 0;
+}
diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
index 9f5a6c6..f14a845 100644
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -74,6 +74,7 @@
 #include <asm/vpmu.h>
 #include <public/arch-x86/cpuid.h>
 #include <xsm/xsm.h>
+#include <xen/hvm/deprivileged.h>
 
 /*
  * opt_nmi: one of 'ignore', 'dom0', or 'fatal'.
@@ -500,6 +501,13 @@ static void do_guest_trap(
     struct trap_bounce *tb;
     const struct trap_info *ti;
 
+    /*
+     * If we take the trap whilst in HVM deprivileged mode
+     * then we should crash the domain.
+     */
+    if ( hvm_deprivileged_check_trap(__func__) )
+        return;
+
     trace_pv_trap(trapnr, regs->eip, use_error_code, regs->error_code);
 
     tb = &v->arch.pv_vcpu.trap_bounce;
@@ -617,6 +625,13 @@ static void do_trap(struct cpu_user_regs *regs, int 
use_error_code)
 
     DEBUGGER_trap_entry(trapnr, regs);
 
+    /*
+     * If we take the trap whilst in HVM deprivileged mode
+     * then we should crash the domain.
+     */
+    if ( hvm_deprivileged_check_trap(__func__) )
+        return;
+
     if ( guest_mode(regs) )
     {
         do_guest_trap(trapnr, regs, use_error_code);
@@ -1070,6 +1085,13 @@ void do_invalid_op(struct cpu_user_regs *regs)
 
     DEBUGGER_trap_entry(TRAP_invalid_op, regs);
 
+    /*
+     * If we take the trap whilst in HVM deprivileged mode
+     * then we should crash the domain.
+     */
+    if ( hvm_deprivileged_check_trap(__func__) )
+        return;
+
     if ( likely(guest_mode(regs)) )
     {
         if ( !emulate_invalid_rdtscp(regs) &&
@@ -1159,6 +1181,13 @@ void do_int3(struct cpu_user_regs *regs)
 {
     DEBUGGER_trap_entry(TRAP_int3, regs);
 
+    /*
+     * If we take the trap whilst in HVM deprivileged mode
+     * then we should crash the domain.
+     */
+    if ( hvm_deprivileged_check_trap(__func__) )
+        return;
+
     if ( !guest_mode(regs) )
     {
         debugger_trap_fatal(TRAP_int3, regs);
@@ -1495,9 +1524,14 @@ void do_page_fault(struct cpu_user_regs *regs)
 
     perfc_incr(page_faults);
 
+    /* If we get a page fault whilst in HVM deprivileged mode */
+    if( hvm_deprivileged_check_trap(__func__) )
+        return;
+
     if ( unlikely(fixup_page_fault(addr, regs) != 0) )
         return;
 
+
     if ( unlikely(!guest_mode(regs)) )
     {
         pf_type = spurious_page_fault(addr, regs);
@@ -3225,6 +3259,13 @@ void do_general_protection(struct cpu_user_regs *regs)
 
     DEBUGGER_trap_entry(TRAP_gp_fault, regs);
 
+    /*
+     * If we take the trap whilst in HVM deprivileged mode
+     * then we should crash the domain.
+     */
+    if ( hvm_deprivileged_check_trap(__func__) )
+        return;
+
     if ( regs->error_code & 1 )
         goto hardware_gp;
 
@@ -3490,6 +3531,13 @@ void do_device_not_available(struct cpu_user_regs *regs)
 
     BUG_ON(!guest_mode(regs));
 
+    /*
+     * If we take the trap whilst in HVM deprivileged mode
+     * then we should crash the domain.
+     */
+    if ( hvm_deprivileged_check_trap(__func__) )
+        return;
+
     vcpu_restore_fpu_lazy(curr);
 
     if ( curr->arch.pv_vcpu.ctrlreg[0] & X86_CR0_TS )
@@ -3531,6 +3579,13 @@ void do_debug(struct cpu_user_regs *regs)
 
     DEBUGGER_trap_entry(TRAP_debug, regs);
 
+    /*
+     * If we take the trap whilst in HVM deprivileged mode
+     * then we should crash the domain.
+     */
+    if ( hvm_deprivileged_check_trap(__func__) )
+        return;
+
     if ( !guest_mode(regs) )
     {
         if ( regs->eflags & X86_EFLAGS_TF )
diff --git a/xen/include/xen/hvm/deprivileged.h 
b/xen/include/xen/hvm/deprivileged.h
index 5915224..b6e575d 100644
--- a/xen/include/xen/hvm/deprivileged.h
+++ b/xen/include/xen/hvm/deprivileged.h
@@ -84,7 +84,7 @@ int hvm_deprivileged_prepare_vcpu(struct vcpu *vcpu);
 void hvm_deprivileged_destroy_vcpu(struct vcpu *vcpu);
 
 /* Called to perform a user mode operation. */
-void hvm_deprivileged_user_mode(void);
+int hvm_deprivileged_user_mode(void);
 
 /* Called when the user mode operation has completed */
 void hvm_deprivileged_finish_user_mode(void);
@@ -106,9 +106,32 @@ void hvm_deprivileged_setup_stacks(unsigned long 
stack_ptr);
 /* Use to restore the stacks for deprivileged mode */
 void hvm_deprivileged_restore_stacks(void);
 
+/* Check if we are in deprivileged mode */
+int is_hvm_deprivileged_vcpu(void);
+
 /* The ring 3 code */
 void hvm_deprivileged_ring3(void);
 
+/*
+ * Crash the domain. This should not be called if there are any memory
+ * allocations which will be freed by code following its invocation in the
+ * current execution context (current stack). This is because it causes a
+ * permanent 'context switch' and the current stack will be cloberred so
+ * any allocations made which are not freed by other paths will leak.
+ * This function should only be used after deprivileged mode has been
+ * successfully switched into, otherwise, the normal domain_crash function
+ * should be used.
+ *
+ * The domain which is crashed is that of the current vcpu.
+ */
+void hvm_deprivileged_crash_domain(const char *reason);
+
+/*
+ * Call when inside a trap that should cause a domain crash if in user mode
+ * e.g. an invalid_op is trapped whilst in user mode.
+ */
+int hvm_deprivileged_check_trap(const char* func_name);
+
 /* The segments where the user mode .text and .data are stored */
 extern unsigned long __hvm_deprivileged_text_start[];
 extern unsigned long __hvm_deprivileged_text_end[];
-- 
2.1.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.