diff options
author | André Hentschel <nerv@dawncrow.de> | 2013-06-18 18:23:26 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-06-24 10:21:59 -0400 |
commit | a4780adeefd042482f624f5e0d577bf9cdcbb760 (patch) | |
tree | ead0e79b4f797a26cc30fc1596ada6fd6da5daf6 /arch/arm/kernel | |
parent | 4a1b573346ee0d64d95beb78d49a5bbb574e6c6a (diff) |
ARM: 7735/2: Preserve the user r/w register TPIDRURW on context switch and fork
Since commit 6a1c53124aa1 the user writeable TLS register was zeroed to
prevent it from being used as a covert channel between two tasks.
There are more and more applications coming to Windows RT,
Wine could support them, but mostly they expect to have
the thread environment block (TEB) in TPIDRURW.
This patch preserves that register per thread instead of clearing it.
Unlike the TPIDRURO, which is already switched, the TPIDRURW
can be updated from userspace so needs careful treatment in the case that we
modify TPIDRURW and call fork(). To avoid this we must always read
TPIDRURW in copy_thread.
Signed-off-by: André Hentschel <nerv@dawncrow.de>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Jonathan Austin <jonathan.austin@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/entry-armv.S | 5 | ||||
-rw-r--r-- | arch/arm/kernel/process.c | 4 | ||||
-rw-r--r-- | arch/arm/kernel/ptrace.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 4 |
4 files changed, 9 insertions, 6 deletions
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 582b405befc5..a39cfc2a1f90 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S | |||
@@ -685,15 +685,16 @@ ENTRY(__switch_to) | |||
685 | UNWIND(.fnstart ) | 685 | UNWIND(.fnstart ) |
686 | UNWIND(.cantunwind ) | 686 | UNWIND(.cantunwind ) |
687 | add ip, r1, #TI_CPU_SAVE | 687 | add ip, r1, #TI_CPU_SAVE |
688 | ldr r3, [r2, #TI_TP_VALUE] | ||
689 | ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack | 688 | ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack |
690 | THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack | 689 | THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack |
691 | THUMB( str sp, [ip], #4 ) | 690 | THUMB( str sp, [ip], #4 ) |
692 | THUMB( str lr, [ip], #4 ) | 691 | THUMB( str lr, [ip], #4 ) |
692 | ldr r4, [r2, #TI_TP_VALUE] | ||
693 | ldr r5, [r2, #TI_TP_VALUE + 4] | ||
693 | #ifdef CONFIG_CPU_USE_DOMAINS | 694 | #ifdef CONFIG_CPU_USE_DOMAINS |
694 | ldr r6, [r2, #TI_CPU_DOMAIN] | 695 | ldr r6, [r2, #TI_CPU_DOMAIN] |
695 | #endif | 696 | #endif |
696 | set_tls r3, r4, r5 | 697 | switch_tls r1, r4, r5, r3, r7 |
697 | #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) | 698 | #if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP) |
698 | ldr r7, [r2, #TI_TASK] | 699 | ldr r7, [r2, #TI_TASK] |
699 | ldr r8, =__stack_chk_guard | 700 | ldr r8, =__stack_chk_guard |
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index f21970316836..087064148ebf 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <asm/thread_notify.h> | 39 | #include <asm/thread_notify.h> |
40 | #include <asm/stacktrace.h> | 40 | #include <asm/stacktrace.h> |
41 | #include <asm/mach/time.h> | 41 | #include <asm/mach/time.h> |
42 | #include <asm/tls.h> | ||
42 | 43 | ||
43 | #ifdef CONFIG_CC_STACKPROTECTOR | 44 | #ifdef CONFIG_CC_STACKPROTECTOR |
44 | #include <linux/stackprotector.h> | 45 | #include <linux/stackprotector.h> |
@@ -343,7 +344,8 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start, | |||
343 | clear_ptrace_hw_breakpoint(p); | 344 | clear_ptrace_hw_breakpoint(p); |
344 | 345 | ||
345 | if (clone_flags & CLONE_SETTLS) | 346 | if (clone_flags & CLONE_SETTLS) |
346 | thread->tp_value = childregs->ARM_r3; | 347 | thread->tp_value[0] = childregs->ARM_r3; |
348 | thread->tp_value[1] = get_tpuser(); | ||
347 | 349 | ||
348 | thread_notify(THREAD_NOTIFY_COPY, thread); | 350 | thread_notify(THREAD_NOTIFY_COPY, thread); |
349 | 351 | ||
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 03deeffd9f6d..2bc1514d6dbe 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c | |||
@@ -849,7 +849,7 @@ long arch_ptrace(struct task_struct *child, long request, | |||
849 | #endif | 849 | #endif |
850 | 850 | ||
851 | case PTRACE_GET_THREAD_AREA: | 851 | case PTRACE_GET_THREAD_AREA: |
852 | ret = put_user(task_thread_info(child)->tp_value, | 852 | ret = put_user(task_thread_info(child)->tp_value[0], |
853 | datap); | 853 | datap); |
854 | break; | 854 | break; |
855 | 855 | ||
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 18b32e8e4497..517bfd4da1c9 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
@@ -581,7 +581,7 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs) | |||
581 | return regs->ARM_r0; | 581 | return regs->ARM_r0; |
582 | 582 | ||
583 | case NR(set_tls): | 583 | case NR(set_tls): |
584 | thread->tp_value = regs->ARM_r0; | 584 | thread->tp_value[0] = regs->ARM_r0; |
585 | if (tls_emu) | 585 | if (tls_emu) |
586 | return 0; | 586 | return 0; |
587 | if (has_tls_reg) { | 587 | if (has_tls_reg) { |
@@ -699,7 +699,7 @@ static int get_tp_trap(struct pt_regs *regs, unsigned int instr) | |||
699 | int reg = (instr >> 12) & 15; | 699 | int reg = (instr >> 12) & 15; |
700 | if (reg == 15) | 700 | if (reg == 15) |
701 | return 1; | 701 | return 1; |
702 | regs->uregs[reg] = current_thread_info()->tp_value; | 702 | regs->uregs[reg] = current_thread_info()->tp_value[0]; |
703 | regs->ARM_pc += 4; | 703 | regs->ARM_pc += 4; |
704 | return 0; | 704 | return 0; |
705 | } | 705 | } |