forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									7a0bd49713
								
							
						
					
					
						commit
						fbfb872f5f
					
				
					 4 changed files with 66 additions and 17 deletions
				
			
		| 
						 | 
					@ -1,6 +1,9 @@
 | 
				
			||||||
#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 @@
 | 
				
			||||||
#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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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)
 | 
				
			||||||
	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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -581,7 +581,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))
 | 
				
			||||||
| 
						 | 
					@ -632,21 +631,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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue