Commit 6e0a6583 authored by Nathan Lynch's avatar Nathan Lynch Committed by Greg Kroah-Hartman

ARM: 8148/1: flush TLS and thumbee register state during exec

commit fbfb872f upstream.

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: default avatarNathan Lynch <nathan_lynch@mentor.com>
Acked-by: default avatarWill Deacon <will.deacon@arm.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a47061a1
#ifndef __ASMARM_TLS_H #ifndef __ASMARM_TLS_H
#define __ASMARM_TLS_H #define __ASMARM_TLS_H
#include <linux/compiler.h>
#include <asm/thread_info.h>
#ifdef __ASSEMBLY__ #ifdef __ASSEMBLY__
#include <asm/asm-offsets.h> #include <asm/asm-offsets.h>
.macro switch_tls_none, base, tp, tpuser, tmp1, tmp2 .macro switch_tls_none, base, tp, tpuser, tmp1, tmp2
...@@ -50,6 +53,47 @@ ...@@ -50,6 +53,47 @@
#endif #endif
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
static inline void set_tls(unsigned long val)
{
struct thread_info *thread;
thread = current_thread_info();
thread->tp_value[0] = val;
/*
* This code runs with preemption enabled and therefore must
* be reentrant with respect to switch_tls.
*
* We need to ensure ordering between the shadow state and the
* hardware state, so that we don't corrupt the hardware state
* with a stale shadow state during context switch.
*
* If we're preempted here, switch_tls will load TPIDRURO from
* thread_info upon resuming execution and the following mcr
* is merely redundant.
*/
barrier();
if (!tls_emu) {
if (has_tls_reg) {
asm("mcr p15, 0, %0, c13, c0, 3"
: : "r" (val));
} else {
/*
* User space must never try to access this
* directly. Expect your app to break
* eventually if you do so. The user helper
* at 0xffff0fe0 must be used instead. (see
* entry-armv.S for details)
*/
*((unsigned int *)0xffff0ff0) = val;
}
}
}
static inline unsigned long get_tpuser(void) static inline unsigned long get_tpuser(void)
{ {
unsigned long reg = 0; unsigned long reg = 0;
...@@ -59,5 +103,23 @@ static inline unsigned long get_tpuser(void) ...@@ -59,5 +103,23 @@ static inline unsigned long get_tpuser(void)
return reg; return reg;
} }
static inline void set_tpuser(unsigned long val)
{
/* Since TPIDRURW is fully context-switched (unlike TPIDRURO),
* we need not update thread_info.
*/
if (has_tls_reg && !tls_emu) {
asm("mcr p15, 0, %0, c13, c0, 2"
: : "r" (val));
}
}
static inline void flush_tls(void)
{
set_tls(0);
set_tpuser(0);
}
#endif #endif
#endif /* __ASMARM_TLS_H */ #endif /* __ASMARM_TLS_H */
...@@ -334,6 +334,8 @@ void flush_thread(void) ...@@ -334,6 +334,8 @@ void flush_thread(void)
memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
memset(&thread->fpstate, 0, sizeof(union fp_state)); memset(&thread->fpstate, 0, sizeof(union fp_state));
flush_tls();
thread_notify(THREAD_NOTIFY_FLUSH, thread); thread_notify(THREAD_NOTIFY_FLUSH, thread);
} }
......
...@@ -45,7 +45,7 @@ static int thumbee_notifier(struct notifier_block *self, unsigned long cmd, void ...@@ -45,7 +45,7 @@ static int thumbee_notifier(struct notifier_block *self, unsigned long cmd, void
switch (cmd) { switch (cmd) {
case THREAD_NOTIFY_FLUSH: case THREAD_NOTIFY_FLUSH:
thread->thumbee_state = 0; teehbr_write(0);
break; break;
case THREAD_NOTIFY_SWITCH: case THREAD_NOTIFY_SWITCH:
current_thread_info()->thumbee_state = teehbr_read(); current_thread_info()->thumbee_state = teehbr_read();
......
...@@ -579,7 +579,6 @@ do_cache_op(unsigned long start, unsigned long end, int flags) ...@@ -579,7 +579,6 @@ do_cache_op(unsigned long start, unsigned long end, int flags)
#define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE) #define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE)
asmlinkage int arm_syscall(int no, struct pt_regs *regs) asmlinkage int arm_syscall(int no, struct pt_regs *regs)
{ {
struct thread_info *thread = current_thread_info();
siginfo_t info; siginfo_t info;
if ((no >> 16) != (__ARM_NR_BASE>> 16)) if ((no >> 16) != (__ARM_NR_BASE>> 16))
...@@ -630,21 +629,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) ...@@ -630,21 +629,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)
return regs->ARM_r0; return regs->ARM_r0;
case NR(set_tls): case NR(set_tls):
thread->tp_value[0] = regs->ARM_r0; set_tls(regs->ARM_r0);
if (tls_emu)
return 0;
if (has_tls_reg) {
asm ("mcr p15, 0, %0, c13, c0, 3"
: : "r" (regs->ARM_r0));
} else {
/*
* User space must never try to access this directly.
* Expect your app to break eventually if you do so.
* The user helper at 0xffff0fe0 must be used instead.
* (see entry-armv.S for details)
*/
*((unsigned int *)0xffff0ff0) = regs->ARM_r0;
}
return 0; return 0;
#ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG #ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment