diff options
| author | Nathan Lynch <nathan_lynch@mentor.com> | 2014-09-10 21:49:08 -0400 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2014-09-16 11:09:32 -0400 |
| commit | fbfb872f5f417cea48760c535e0ff027c88b507a (patch) | |
| tree | 269d5a514efd78c857e5d044860e5bcc6de3644c | |
| parent | 7a0bd49713aca3040099e1413d1cc9f08802d97a (diff) | |
ARM: 8148/1: flush TLS and thumbee register state during exec
The TPIDRURO and TPIDRURW registers need to be flushed during exec;
otherwise TLS information is potentially leaked. TPIDRURO in
particular needs careful treatment. Since flush_thread basically
needs the same code used to set the TLS in arm_syscall, pull that into
a common set_tls helper in tls.h and use it in both places.
Similarly, TEEHBR needs to be cleared during exec as well. Clearing
its save slot in thread_info isn't right as there is no guarantee
that a thread switch will occur before the new program runs. Just
setting the register directly is sufficient.
Signed-off-by: Nathan Lynch <nathan_lynch@mentor.com>
Acked-by: Will Deacon <will.deacon@arm.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
| -rw-r--r-- | arch/arm/include/asm/tls.h | 62 | ||||
| -rw-r--r-- | arch/arm/kernel/process.c | 2 | ||||
| -rw-r--r-- | arch/arm/kernel/thumbee.c | 2 | ||||
| -rw-r--r-- | arch/arm/kernel/traps.c | 17 |
4 files changed, 66 insertions, 17 deletions
diff --git a/arch/arm/include/asm/tls.h b/arch/arm/include/asm/tls.h index 83259b873333..36172adda9d0 100644 --- a/arch/arm/include/asm/tls.h +++ b/arch/arm/include/asm/tls.h | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | #ifndef __ASMARM_TLS_H | 1 | #ifndef __ASMARM_TLS_H |
| 2 | #define __ASMARM_TLS_H | 2 | #define __ASMARM_TLS_H |
| 3 | 3 | ||
| 4 | #include <linux/compiler.h> | ||
| 5 | #include <asm/thread_info.h> | ||
| 6 | |||
| 4 | #ifdef __ASSEMBLY__ | 7 | #ifdef __ASSEMBLY__ |
| 5 | #include <asm/asm-offsets.h> | 8 | #include <asm/asm-offsets.h> |
| 6 | .macro switch_tls_none, base, tp, tpuser, tmp1, tmp2 | 9 | .macro switch_tls_none, base, tp, tpuser, tmp1, tmp2 |
| @@ -50,6 +53,47 @@ | |||
| 50 | #endif | 53 | #endif |
| 51 | 54 | ||
| 52 | #ifndef __ASSEMBLY__ | 55 | #ifndef __ASSEMBLY__ |
| 56 | |||
| 57 | static inline void set_tls(unsigned long val) | ||
| 58 | { | ||
| 59 | struct thread_info *thread; | ||
| 60 | |||
| 61 | thread = current_thread_info(); | ||
| 62 | |||
| 63 | thread->tp_value[0] = val; | ||
| 64 | |||
| 65 | /* | ||
| 66 | * This code runs with preemption enabled and therefore must | ||
| 67 | * be reentrant with respect to switch_tls. | ||
| 68 | * | ||
| 69 | * We need to ensure ordering between the shadow state and the | ||
| 70 | * hardware state, so that we don't corrupt the hardware state | ||
| 71 | * with a stale shadow state during context switch. | ||
| 72 | * | ||
| 73 | * If we're preempted here, switch_tls will load TPIDRURO from | ||
| 74 | * thread_info upon resuming execution and the following mcr | ||
| 75 | * is merely redundant. | ||
| 76 | */ | ||
| 77 | barrier(); | ||
| 78 | |||
| 79 | if (!tls_emu) { | ||
| 80 | if (has_tls_reg) { | ||
| 81 | asm("mcr p15, 0, %0, c13, c0, 3" | ||
| 82 | : : "r" (val)); | ||
| 83 | } else { | ||
| 84 | /* | ||
| 85 | * User space must never try to access this | ||
| 86 | * directly. Expect your app to break | ||
| 87 | * eventually if you do so. The user helper | ||
| 88 | * at 0xffff0fe0 must be used instead. (see | ||
| 89 | * entry-armv.S for details) | ||
| 90 | */ | ||
| 91 | *((unsigned int *)0xffff0ff0) = val; | ||
| 92 | } | ||
| 93 | |||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 53 | static inline unsigned long get_tpuser(void) | 97 | static inline unsigned long get_tpuser(void) |
| 54 | { | 98 | { |
| 55 | unsigned long reg = 0; | 99 | unsigned long reg = 0; |
| @@ -59,5 +103,23 @@ static inline unsigned long get_tpuser(void) | |||
| 59 | 103 | ||
| 60 | return reg; | 104 | return reg; |
| 61 | } | 105 | } |
| 106 | |||
| 107 | static inline void set_tpuser(unsigned long val) | ||
| 108 | { | ||
| 109 | /* Since TPIDRURW is fully context-switched (unlike TPIDRURO), | ||
| 110 | * we need not update thread_info. | ||
| 111 | */ | ||
| 112 | if (has_tls_reg && !tls_emu) { | ||
| 113 | asm("mcr p15, 0, %0, c13, c0, 2" | ||
| 114 | : : "r" (val)); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | static inline void flush_tls(void) | ||
| 119 | { | ||
| 120 | set_tls(0); | ||
| 121 | set_tpuser(0); | ||
| 122 | } | ||
| 123 | |||
| 62 | #endif | 124 | #endif |
| 63 | #endif /* __ASMARM_TLS_H */ | 125 | #endif /* __ASMARM_TLS_H */ |
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 81ef686a91ca..a35f6ebbd2c2 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
| @@ -334,6 +334,8 @@ void flush_thread(void) | |||
| 334 | memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); | 334 | memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); |
| 335 | memset(&thread->fpstate, 0, sizeof(union fp_state)); | 335 | memset(&thread->fpstate, 0, sizeof(union fp_state)); |
| 336 | 336 | ||
| 337 | flush_tls(); | ||
| 338 | |||
| 337 | thread_notify(THREAD_NOTIFY_FLUSH, thread); | 339 | thread_notify(THREAD_NOTIFY_FLUSH, thread); |
| 338 | } | 340 | } |
| 339 | 341 | ||
diff --git a/arch/arm/kernel/thumbee.c b/arch/arm/kernel/thumbee.c index 7b8403b76666..80f0d69205e7 100644 --- a/arch/arm/kernel/thumbee.c +++ b/arch/arm/kernel/thumbee.c | |||
| @@ -45,7 +45,7 @@ static int thumbee_notifier(struct notifier_block *self, unsigned long cmd, void | |||
| 45 | 45 | ||
| 46 | switch (cmd) { | 46 | switch (cmd) { |
| 47 | case THREAD_NOTIFY_FLUSH: | 47 | case THREAD_NOTIFY_FLUSH: |
| 48 | thread->thumbee_state = 0; | 48 | teehbr_write(0); |
| 49 | break; | 49 | break; |
| 50 | case THREAD_NOTIFY_SWITCH: | 50 | case THREAD_NOTIFY_SWITCH: |
| 51 | current_thread_info()->thumbee_state = teehbr_read(); | 51 | current_thread_info()->thumbee_state = teehbr_read(); |
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index c8e4bb714944..a964c9f40f87 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
| @@ -581,7 +581,6 @@ do_cache_op(unsigned long start, unsigned long end, int flags) | |||
| 581 | #define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE) | 581 | #define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE) |
| 582 | asmlinkage int arm_syscall(int no, struct pt_regs *regs) | 582 | asmlinkage int arm_syscall(int no, struct pt_regs *regs) |
| 583 | { | 583 | { |
| 584 | struct thread_info *thread = current_thread_info(); | ||
| 585 | siginfo_t info; | 584 | siginfo_t info; |
| 586 | 585 | ||
| 587 | if ((no >> 16) != (__ARM_NR_BASE>> 16)) | 586 | if ((no >> 16) != (__ARM_NR_BASE>> 16)) |
| @@ -632,21 +631,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) | |||
| 632 | return regs->ARM_r0; | 631 | return regs->ARM_r0; |
| 633 | 632 | ||
| 634 | case NR(set_tls): | 633 | case NR(set_tls): |
| 635 | thread->tp_value[0] = regs->ARM_r0; | 634 | set_tls(regs->ARM_r0); |
| 636 | if (tls_emu) | ||
| 637 | return 0; | ||
| 638 | if (has_tls_reg) { | ||
| 639 | asm ("mcr p15, 0, %0, c13, c0, 3" | ||
| 640 | : : "r" (regs->ARM_r0)); | ||
| 641 | } else { | ||
| 642 | /* | ||
| 643 | * User space must never try to access this directly. | ||
| 644 | * Expect your app to break eventually if you do so. | ||
| 645 | * The user helper at 0xffff0fe0 must be used instead. | ||
| 646 | * (see entry-armv.S for details) | ||
| 647 | */ | ||
| 648 | *((unsigned int *)0xffff0ff0) = regs->ARM_r0; | ||
| 649 | } | ||
| 650 | return 0; | 635 | return 0; |
| 651 | 636 | ||
| 652 | #ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG | 637 | #ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG |
