aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Martin <Dave.Martin@arm.com>2017-06-21 11:00:44 -0400
committerWill Deacon <will.deacon@arm.com>2017-06-22 10:58:20 -0400
commit936eb65ca22ad856cb3a995e8cd742e982dc2dd0 (patch)
treee779288573ebdabdf5248291fab6c436afd4a4f7
parente1d5a8fb73e6c65280c21ec188180345649ee650 (diff)
arm64: ptrace: Flush user-RW TLS reg to thread_struct before reading
When reading current's user-writable TLS register (which occurs when dumping core for native tasks), it is possible that userspace has modified it since the time the task was last scheduled out. The new TLS register value is not guaranteed to have been written immediately back to thread_struct in this case. As a result, a coredump can capture stale data for this register. Reading the register for a stopped task via ptrace is unaffected. For native tasks, this patch explicitly flushes the TPIDR_EL0 register back to thread_struct before dumping when operating on current, thus ensuring that coredump contents are up to date. For compat tasks, the TLS register is not user-writable and so cannot be out of sync, so no flush is required in compat_tls_get(). Signed-off-by: Dave Martin <Dave.Martin@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--arch/arm64/include/asm/processor.h3
-rw-r--r--arch/arm64/kernel/process.c8
-rw-r--r--arch/arm64/kernel/ptrace.c4
3 files changed, 13 insertions, 2 deletions
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 9428b93fefb2..64c9e78f9882 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -104,6 +104,9 @@ struct thread_struct {
104#define task_user_tls(t) (&(t)->thread.tp_value) 104#define task_user_tls(t) (&(t)->thread.tp_value)
105#endif 105#endif
106 106
107/* Sync TPIDR_EL0 back to thread_struct for current */
108void tls_preserve_current_state(void);
109
107#define INIT_THREAD { } 110#define INIT_THREAD { }
108 111
109static inline void start_thread_common(struct pt_regs *regs, unsigned long pc) 112static inline void start_thread_common(struct pt_regs *regs, unsigned long pc)
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index af1ea258c212..659ae8094ed5 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -298,12 +298,16 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
298 return 0; 298 return 0;
299} 299}
300 300
301void tls_preserve_current_state(void)
302{
303 *task_user_tls(current) = read_sysreg(tpidr_el0);
304}
305
301static void tls_thread_switch(struct task_struct *next) 306static void tls_thread_switch(struct task_struct *next)
302{ 307{
303 unsigned long tpidr, tpidrro; 308 unsigned long tpidr, tpidrro;
304 309
305 tpidr = read_sysreg(tpidr_el0); 310 tls_preserve_current_state();
306 *task_user_tls(current) = tpidr;
307 311
308 tpidr = *task_user_tls(next); 312 tpidr = *task_user_tls(next);
309 tpidrro = is_compat_thread(task_thread_info(next)) ? 313 tpidrro = is_compat_thread(task_thread_info(next)) ?
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index eeef01a219a6..35846f1550db 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -652,6 +652,10 @@ static int tls_get(struct task_struct *target, const struct user_regset *regset,
652 void *kbuf, void __user *ubuf) 652 void *kbuf, void __user *ubuf)
653{ 653{
654 unsigned long *tls = &target->thread.tp_value; 654 unsigned long *tls = &target->thread.tp_value;
655
656 if (target == current)
657 tls_preserve_current_state();
658
655 return user_regset_copyout(&pos, &count, &kbuf, &ubuf, tls, 0, -1); 659 return user_regset_copyout(&pos, &count, &kbuf, &ubuf, tls, 0, -1);
656} 660}
657 661