diff mbox

[2/2] target-arm: Ignore attempts to set invalid modes in CPSR

Message ID 1324753147-5747-3-git-send-email-peter.maydell@linaro.org
State Accepted
Commit 37064a8b6f9075e18b05bfc6d5264b138a224713
Headers show

Commit Message

Peter Maydell Dec. 24, 2011, 6:59 p.m. UTC
Ignore attempts to set the CPSR mode field to an invalid value.
This is UNPREDICTABLE, but we should not cpu_abort() for things
a malicious guest (or a confused user on the gdbstub interface)
can provoke.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 target-arm/helper.c |   30 +++++++++++++++++++++++++++++-
 1 files changed, 29 insertions(+), 1 deletions(-)
diff mbox

Patch

diff --git a/target-arm/helper.c b/target-arm/helper.c
index 5b994d5..261d547 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -463,6 +463,26 @@  void cpu_arm_close(CPUARMState *env)
     g_free(env);
 }
 
+static int bad_mode_switch(CPUState *env, int mode)
+{
+    /* Return true if it is not valid for us to switch to
+     * this CPU mode (ie all the UNPREDICTABLE cases in
+     * the ARM ARM CPSRWriteByInstr pseudocode).
+     */
+    switch (mode) {
+    case ARM_CPU_MODE_USR:
+    case ARM_CPU_MODE_SYS:
+    case ARM_CPU_MODE_SVC:
+    case ARM_CPU_MODE_ABT:
+    case ARM_CPU_MODE_UND:
+    case ARM_CPU_MODE_IRQ:
+    case ARM_CPU_MODE_FIQ:
+        return 0;
+    default:
+        return 1;
+    }
+}
+
 uint32_t cpsr_read(CPUARMState *env)
 {
     int ZF;
@@ -499,7 +519,15 @@  void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
     }
 
     if ((env->uncached_cpsr ^ val) & mask & CPSR_M) {
-        switch_mode(env, val & CPSR_M);
+        if (bad_mode_switch(env, val & CPSR_M)) {
+            /* Attempt to switch to an invalid mode: this is UNPREDICTABLE.
+             * We choose to ignore the attempt and leave the CPSR M field
+             * untouched.
+             */
+            mask &= ~CPSR_M;
+        } else {
+            switch_mode(env, val & CPSR_M);
+        }
     }
     mask &= ~CACHED_CPSR_BITS;
     env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);