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 0ef0093800f25c90654d4853f925fc016b6b290c..e9c9a117bd25d5b69e8ba5313990f18f8db7311d 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 9a8ce342cd820cfe6b097725a67367f2b839fcf7..608008229c7db6881f8d103750153280d96105d7 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 0d6f5413be18663ad1bb44ec151f424a6ab270c8..bc5f50799d75627fb3eb22b9afe6d849373e77d4 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 5e1d029147cb9d645afe359a2e374ef36807d956..6391728c8f0377019404c6a6744df1e24697b3a8 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 f1c720c0d5689519fa9ff559a940fe9db5ae4443..612eb530f33fcd19bc4539facb26fc30a2583979 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";
This page took 0.030452 seconds and 5 git commands to generate.