ARM: save and reset the address limit when entering an exception
authorRussell King <rmk+kernel@armlinux.org.uk>
Fri, 13 May 2016 10:40:20 +0000 (11:40 +0100)
committerRussell King <rmk+kernel@armlinux.org.uk>
Thu, 7 Jul 2016 15:01:01 +0000 (16:01 +0100)
When we enter an exception, the current address limit should not apply
to the exception context: if the exception context wishes to access
kernel space via the user accessors (eg, perf code), it must explicitly
request such access.

Acked-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
arch/arm/include/asm/ptrace.h
arch/arm/kernel/asm-offsets.c
arch/arm/kernel/entry-armv.S
arch/arm/kernel/entry-header.S
arch/arm/kernel/process.c

index 0ef0093..e9c9a11 100644 (file)
@@ -22,7 +22,7 @@ struct pt_regs {
 struct svc_pt_regs {
        struct pt_regs regs;
        u32 dacr;
-       u32 unused;
+       u32 addr_limit;
 };
 
 #define to_svc_pt_regs(r) container_of(r, struct svc_pt_regs, regs)
index 9a8ce34..6080082 100644 (file)
@@ -109,6 +109,7 @@ int main(void)
   DEFINE(S_OLD_R0,             offsetof(struct pt_regs, ARM_ORIG_r0));
   DEFINE(PT_REGS_SIZE,         sizeof(struct pt_regs));
   DEFINE(SVC_DACR,             offsetof(struct svc_pt_regs, dacr));
+  DEFINE(SVC_ADDR_LIMIT,       offsetof(struct svc_pt_regs, addr_limit));
   DEFINE(SVC_REGS_SIZE,                sizeof(struct svc_pt_regs));
   BLANK();
 #ifdef CONFIG_CACHE_L2X0
index 0d6f541..bc5f507 100644 (file)
@@ -185,6 +185,12 @@ ENDPROC(__und_invalid)
        @
        stmia   r7, {r2 - r6}
 
+       get_thread_info tsk
+       ldr     r0, [tsk, #TI_ADDR_LIMIT]
+       mov     r1, #TASK_SIZE
+       str     r1, [tsk, #TI_ADDR_LIMIT]
+       str     r0, [sp, #SVC_ADDR_LIMIT]
+
        uaccess_save r0
        .if \uaccess
        uaccess_disable r0
@@ -213,7 +219,6 @@ __irq_svc:
        irq_handler
 
 #ifdef CONFIG_PREEMPT
-       get_thread_info tsk
        ldr     r8, [tsk, #TI_PREEMPT]          @ get preempt count
        ldr     r0, [tsk, #TI_FLAGS]            @ get flags
        teq     r8, #0                          @ if preempt count != 0
index 5e1d029..6391728 100644 (file)
        blne    trace_hardirqs_off
 #endif
        .endif
+       ldr     r1, [sp, #SVC_ADDR_LIMIT]
        uaccess_restore
+       str     r1, [tsk, #TI_ADDR_LIMIT]
 
 #ifndef CONFIG_THUMB2_KERNEL
        @ ARM mode SVC restore
        @ on the stack remains correct).
        @
        .macro  svc_exit_via_fiq
+       ldr     r1, [sp, #SVC_ADDR_LIMIT]
        uaccess_restore
+       str     r1, [tsk, #TI_ADDR_LIMIT]
 #ifndef CONFIG_THUMB2_KERNEL
        @ ARM mode restore
        mov     r0, sp
index f1c720c..612eb53 100644 (file)
@@ -96,19 +96,23 @@ void __show_regs(struct pt_regs *regs)
        unsigned long flags;
        char buf[64];
 #ifndef CONFIG_CPU_V7M
-       unsigned int domain;
+       unsigned int domain, fs;
 #ifdef CONFIG_CPU_SW_DOMAIN_PAN
        /*
         * Get the domain register for the parent context. In user
         * mode, we don't save the DACR, so lets use what it should
         * be. For other modes, we place it after the pt_regs struct.
         */
-       if (user_mode(regs))
+       if (user_mode(regs)) {
                domain = DACR_UACCESS_ENABLE;
-       else
+               fs = get_fs();
+       } else {
                domain = to_svc_pt_regs(regs)->dacr;
+               fs = to_svc_pt_regs(regs)->addr_limit;
+       }
 #else
        domain = get_domain();
+       fs = get_fs();
 #endif
 #endif
 
@@ -144,7 +148,7 @@ void __show_regs(struct pt_regs *regs)
                if ((domain & domain_mask(DOMAIN_USER)) ==
                    domain_val(DOMAIN_USER, DOMAIN_NOACCESS))
                        segment = "none";
-               else if (get_fs() == get_ds())
+               else if (fs == get_ds())
                        segment = "kernel";
                else
                        segment = "user";