]> xenbits.xensource.com Git - xen.git/commitdiff
xen: arm: Correctly handle exception injection from userspace on 64-bit.
authorIan Campbell <ian.campbell@citrix.com>
Tue, 12 Aug 2014 13:48:28 +0000 (15:48 +0200)
committerJan Beulich <jbeulich@suse.com>
Tue, 12 Aug 2014 13:48:28 +0000 (15:48 +0200)
Firstly we must be prepared to propagate traps from 32-bit userspace even for
64-bit guests, so wrap the existing inject_undef??_exception into
inject_undef_exception and use that when injecting an undef exception. The
various other exception cases (aborts etc) already do this.

Secondly when injecting the trap we must pick the correct exception vector
depending on whether the source of the trap was 32-bit EL0, 64-bit EL0 or EL1.

This is part of CVE-2014-5147 / XSA-102.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
Acked-by: Julien Grall <julien.grall@linaro.org>
master commit: a98669781769e821413dfef4ef99b93171375610
master date: 2014-08-12 15:36:15 +0200

xen/arch/arm/traps.c
xen/include/asm-arm/processor.h

index 7367399f9b3467f8c05451f47128c24d6a89fe1a..653620968d0a50922d98cf29ff1f6acee7799008 100644 (file)
@@ -286,7 +286,7 @@ static void cpsr_switch_mode(struct cpu_user_regs *regs, int mode)
         regs->cpsr |= PSR_BIG_ENDIAN;
 }
 
-static vaddr_t exception_handler(vaddr_t offset)
+static vaddr_t exception_handler32(vaddr_t offset)
 {
     uint32_t sctlr = READ_SYSREG32(SCTLR_EL1);
 
@@ -318,7 +318,7 @@ static void inject_undef32_exception(struct cpu_user_regs *regs)
     regs->lr_und = regs->pc32 + return_offset;
 
     /* Branch to exception vector */
-    regs->pc32 = exception_handler(VECTOR32_UND);
+    regs->pc32 = exception_handler32(VECTOR32_UND);
 }
 
 /* Injects an Abort exception into the current vcpu, PC is the exact
@@ -344,7 +344,7 @@ static void inject_abt32_exception(struct cpu_user_regs *regs,
     regs->spsr_abt = spsr;
     regs->lr_abt = regs->pc32 + return_offset;
 
-    regs->pc32 = exception_handler(prefetch ? VECTOR32_PABT : VECTOR32_DABT);
+    regs->pc32 = exception_handler32(prefetch ? VECTOR32_PABT : VECTOR32_DABT);
 
     /* Inject a debug fault, best we can do right now */
     if ( READ_SYSREG(TCR_EL1) & TTBCR_EAE )
@@ -397,9 +397,28 @@ static void inject_pabt32_exception(struct cpu_user_regs *regs,
 }
 
 #ifdef CONFIG_ARM_64
+/*
+ * Take care to call this while regs contains the original faulting
+ * state and not the (partially constructed) exception state.
+ */
+static vaddr_t exception_handler64(struct cpu_user_regs *regs, vaddr_t offset)
+{
+    vaddr_t base = READ_SYSREG(VBAR_EL1);
+
+    if ( usr_mode(regs) )
+        base += VECTOR64_LOWER32_BASE;
+    else if ( psr_mode(regs->cpsr,PSR_MODE_EL0t) )
+        base += VECTOR64_LOWER64_BASE;
+    else /* Otherwise must be from kernel mode */
+        base += VECTOR64_CURRENT_SPx_BASE;
+
+    return base + offset;
+}
+
 /* Inject an undefined exception into a 64 bit guest */
 static void inject_undef64_exception(struct cpu_user_regs *regs, int instr_len)
 {
+    vaddr_t handler;
     union hsr esr = {
         .iss = 0,
         .len = instr_len,
@@ -408,12 +427,14 @@ static void inject_undef64_exception(struct cpu_user_regs *regs, int instr_len)
 
     BUG_ON( is_pv32_domain(current->domain) );
 
+    handler = exception_handler64(regs, VECTOR64_SYNC_OFFSET);
+
     regs->spsr_el1 = regs->cpsr;
     regs->elr_el1 = regs->pc;
 
     regs->cpsr = PSR_MODE_EL1h | PSR_ABT_MASK | PSR_FIQ_MASK | \
         PSR_IRQ_MASK | PSR_DBG_MASK;
-    regs->pc = READ_SYSREG(VBAR_EL1) + VECTOR64_CURRENT_SPx_SYNC;
+    regs->pc = handler;
 
     WRITE_SYSREG32(esr.bits, ESR_EL1);
 }
@@ -424,6 +445,7 @@ static void inject_abt64_exception(struct cpu_user_regs *regs,
                                    register_t addr,
                                    int instr_len)
 {
+    vaddr_t handler;
     union hsr esr = {
         .iss = 0,
         .len = instr_len,
@@ -445,12 +467,14 @@ static void inject_abt64_exception(struct cpu_user_regs *regs,
 
     BUG_ON( is_pv32_domain(current->domain) );
 
+    handler = exception_handler64(regs, VECTOR64_SYNC_OFFSET);
+
     regs->spsr_el1 = regs->cpsr;
     regs->elr_el1 = regs->pc;
 
     regs->cpsr = PSR_MODE_EL1h | PSR_ABT_MASK | PSR_FIQ_MASK | \
         PSR_IRQ_MASK | PSR_DBG_MASK;
-    regs->pc = READ_SYSREG(VBAR_EL1) + VECTOR64_CURRENT_SPx_SYNC;
+    regs->pc = handler;
 
     WRITE_SYSREG(addr, FAR_EL1);
     WRITE_SYSREG32(esr.bits, ESR_EL1);
@@ -472,6 +496,17 @@ static void inject_iabt64_exception(struct cpu_user_regs *regs,
 
 #endif
 
+static void inject_undef_exception(struct cpu_user_regs *regs,
+                                   int instr_len)
+{
+        if ( is_pv32_domain(current->domain) )
+            inject_undef32_exception(regs);
+#ifdef CONFIG_ARM_64
+        else
+            inject_undef64_exception(regs, instr_len);
+#endif
+}
+
 static void inject_iabt_exception(struct cpu_user_regs *regs,
                                   register_t addr,
                                   int instr_len)
@@ -1440,7 +1475,7 @@ static void do_cp15_32(struct cpu_user_regs *regs,
         gdprintk(XENLOG_ERR, "unhandled 32-bit CP15 access %#x\n",
                  hsr.bits & HSR_CP32_REGS_MASK);
 #endif
-        inject_undef32_exception(regs);
+        inject_undef_exception(regs, hsr.len);
         return;
     }
     advance_pc(regs, hsr);
@@ -1477,7 +1512,7 @@ static void do_cp15_64(struct cpu_user_regs *regs,
             gdprintk(XENLOG_ERR, "unhandled 64-bit CP15 access %#x\n",
                      hsr.bits & HSR_CP64_REGS_MASK);
 #endif
-            inject_undef32_exception(regs);
+            inject_undef_exception(regs, hsr.len);
             return;
         }
     }
@@ -1546,7 +1581,7 @@ bad_cp:
         gdprintk(XENLOG_ERR, "unhandled 32-bit cp14 access %#x\n",
                  hsr.bits & HSR_CP32_REGS_MASK);
 #endif
-        inject_undef32_exception(regs);
+        inject_undef_exception(regs, hsr.len);
         return;
     }
 
@@ -1561,7 +1596,7 @@ static void do_cp14_dbg(struct cpu_user_regs *regs, union hsr hsr)
         return;
     }
 
-    inject_undef32_exception(regs);
+    inject_undef_exception(regs, hsr.len);
 }
 
 static void do_cp(struct cpu_user_regs *regs, union hsr hsr)
@@ -1572,7 +1607,7 @@ static void do_cp(struct cpu_user_regs *regs, union hsr hsr)
         return;
     }
 
-    inject_undef32_exception(regs);
+    inject_undef_exception(regs, hsr.len);
 }
 
 #ifdef CONFIG_ARM_64
@@ -1647,7 +1682,7 @@ static void do_sysreg(struct cpu_user_regs *regs,
             gdprintk(XENLOG_ERR, "unhandled 64-bit sysreg access %#x\n",
                      hsr.bits & HSR_SYSREG_REGS_MASK);
 #endif
-            inject_undef64_exception(regs, sysreg.len);
+            inject_undef_exception(regs, sysreg.len);
         }
     }
 
index 9267c1b0ed2f12ac54603a31fd970187c2978664..1f158ce7aca585991a3ef42a3256c2b5c1595e1d 100644 (file)
@@ -432,14 +432,16 @@ union hsr {
 #define VECTOR32_PABT 12
 #define VECTOR32_DABT 16
 /* ... ARM64 */
-#define VECTOR64_CURRENT_SP0_SYNC  0x000
-#define VECTOR64_CURRENT_SP0_IRQ   0x080
-#define VECTOR64_CURRENT_SP0_FIQ   0x100
-#define VECTOR64_CURRENT_SP0_ERROR 0x180
-#define VECTOR64_CURRENT_SPx_SYNC  0x200
-#define VECTOR64_CURRENT_SPx_IRQ   0x280
-#define VECTOR64_CURRENT_SPx_FIQ   0x300
-#define VECTOR64_CURRENT_SPx_ERROR 0x380
+#define VECTOR64_CURRENT_SP0_BASE  0x000
+#define VECTOR64_CURRENT_SPx_BASE  0x200
+#define VECTOR64_LOWER64_BASE      0x400
+#define VECTOR64_LOWER32_BASE      0x600
+
+#define VECTOR64_SYNC_OFFSET       0x000
+#define VECTOR64_IRQ_OFFSET        0x080
+#define VECTOR64_FIQ_OFFSET        0x100
+#define VECTOR64_ERROR_OFFSET      0x180
+
 
 #if defined(CONFIG_ARM_32)
 # include <asm/arm32/processor.h>