]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
arm64: ptrace: Flush user-RW TLS reg to thread_struct before reading
authorDave Martin <Dave.Martin@arm.com>
Wed, 21 Jun 2017 15:00:44 +0000 (16:00 +0100)
committerWill Deacon <will.deacon@arm.com>
Thu, 22 Jun 2017 14:58:20 +0000 (15:58 +0100)
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>
arch/arm64/include/asm/processor.h
arch/arm64/kernel/process.c
arch/arm64/kernel/ptrace.c

index 9428b93fefb243ed0db90120a0f64c2e4abb35d5..64c9e78f9882ca1551efcec7ae5ca41bccd892ed 100644 (file)
@@ -104,6 +104,9 @@ struct thread_struct {
 #define task_user_tls(t)       (&(t)->thread.tp_value)
 #endif
 
+/* Sync TPIDR_EL0 back to thread_struct for current */
+void tls_preserve_current_state(void);
+
 #define INIT_THREAD  { }
 
 static inline void start_thread_common(struct pt_regs *regs, unsigned long pc)
index af1ea258c2126ce8361af5fd93d1f07d9d34e6de..659ae8094ed5b1bc3f0f1ae6c6b4297062725864 100644 (file)
@@ -298,12 +298,16 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
        return 0;
 }
 
+void tls_preserve_current_state(void)
+{
+       *task_user_tls(current) = read_sysreg(tpidr_el0);
+}
+
 static void tls_thread_switch(struct task_struct *next)
 {
        unsigned long tpidr, tpidrro;
 
-       tpidr = read_sysreg(tpidr_el0);
-       *task_user_tls(current) = tpidr;
+       tls_preserve_current_state();
 
        tpidr = *task_user_tls(next);
        tpidrro = is_compat_thread(task_thread_info(next)) ?
index eeef01a219a6d0ce121255206ebb63691da49196..35846f1550db4eeb9c88da6d876956f0957fd005 100644 (file)
@@ -652,6 +652,10 @@ static int tls_get(struct task_struct *target, const struct user_regset *regset,
                   void *kbuf, void __user *ubuf)
 {
        unsigned long *tls = &target->thread.tp_value;
+
+       if (target == current)
+               tls_preserve_current_state();
+
        return user_regset_copyout(&pos, &count, &kbuf, &ubuf, tls, 0, -1);
 }