Commit 606e9ad2 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'clone3-tls-v5.5-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux

Pull thread fixes from Christian Brauner:
 "This contains a series of patches to fix CLONE_SETTLS when used with
  clone3().

  The clone3() syscall passes the tls argument through struct clone_args
  instead of a register. This means, all architectures that do not
  implement copy_thread_tls() but still support CLONE_SETTLS via
  copy_thread() expecting the tls to be located in a register argument
  based on clone() are currently unfortunately broken. Their tls value
  will be garbage.

  The patch series fixes this on all architectures that currently define
  __ARCH_WANT_SYS_CLONE3. It also adds a compile-time check to ensure
  that any architecture that enables clone3() in the future is forced to
  also implement copy_thread_tls().

  My ultimate goal is to get rid of the copy_thread()/copy_thread_tls()
  split and just have copy_thread_tls() at some point in the not too
  distant future (Maybe even renaming copy_thread_tls() back to simply
  copy_thread() once the old function is ripped from all arches). This
  is dependent now on all arches supporting clone3().

  While all relevant arches do that now there are still four missing:
  ia64, m68k, sh and sparc. They have the system call reserved, but not
  implemented. Once they all implement clone3() we can get rid of
  ARCH_WANT_SYS_CLONE3 and HAVE_COPY_THREAD_TLS.

  This series also includes a minor fix for the arm64 uapi headers which
  caused __NR_clone3 to be missing from the exported user headers.

  Unfortunately the series came in a little late especially given that
  it touches a range of architectures. Due to the holidays not all arch
  maintainers responded in time probably due to their backlog. Will and
  Arnd have thankfully acked the arm specific changes.

  Given that the changes are straightforward and rather minimal combined
  with the fact the that clone3() with CLONE_SETTLS is broken I decided
  to send them post rc3 nonetheless"

* tag 'clone3-tls-v5.5-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux:
  um: Implement copy_thread_tls
  clone3: ensure copy_thread_tls is implemented
  xtensa: Implement copy_thread_tls
  riscv: Implement copy_thread_tls
  parisc: Implement copy_thread_tls
  arm: Implement copy_thread_tls
  arm64: Implement copy_thread_tls
  arm64: Move __ARCH_WANT_SYS_CLONE3 definition to uapi headers
parents ac61145a 457677c7
...@@ -72,6 +72,7 @@ config ARM ...@@ -72,6 +72,7 @@ config ARM
select HAVE_ARM_SMCCC if CPU_V7 select HAVE_ARM_SMCCC if CPU_V7
select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32 select HAVE_EBPF_JIT if !CPU_ENDIAN_BE32
select HAVE_CONTEXT_TRACKING select HAVE_CONTEXT_TRACKING
select HAVE_COPY_THREAD_TLS
select HAVE_C_RECORDMCOUNT select HAVE_C_RECORDMCOUNT
select HAVE_DEBUG_KMEMLEAK select HAVE_DEBUG_KMEMLEAK
select HAVE_DMA_CONTIGUOUS if MMU select HAVE_DMA_CONTIGUOUS if MMU
......
...@@ -226,8 +226,8 @@ void release_thread(struct task_struct *dead_task) ...@@ -226,8 +226,8 @@ void release_thread(struct task_struct *dead_task)
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
int int
copy_thread(unsigned long clone_flags, unsigned long stack_start, copy_thread_tls(unsigned long clone_flags, unsigned long stack_start,
unsigned long stk_sz, struct task_struct *p) unsigned long stk_sz, struct task_struct *p, unsigned long tls)
{ {
struct thread_info *thread = task_thread_info(p); struct thread_info *thread = task_thread_info(p);
struct pt_regs *childregs = task_pt_regs(p); struct pt_regs *childregs = task_pt_regs(p);
...@@ -261,7 +261,7 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start, ...@@ -261,7 +261,7 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,
clear_ptrace_hw_breakpoint(p); clear_ptrace_hw_breakpoint(p);
if (clone_flags & CLONE_SETTLS) if (clone_flags & CLONE_SETTLS)
thread->tp_value[0] = childregs->ARM_r3; thread->tp_value[0] = tls;
thread->tp_value[1] = get_tpuser(); thread->tp_value[1] = get_tpuser();
thread_notify(THREAD_NOTIFY_COPY, thread); thread_notify(THREAD_NOTIFY_COPY, thread);
......
...@@ -138,6 +138,7 @@ config ARM64 ...@@ -138,6 +138,7 @@ config ARM64
select HAVE_CMPXCHG_DOUBLE select HAVE_CMPXCHG_DOUBLE
select HAVE_CMPXCHG_LOCAL select HAVE_CMPXCHG_LOCAL
select HAVE_CONTEXT_TRACKING select HAVE_CONTEXT_TRACKING
select HAVE_COPY_THREAD_TLS
select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_BUGVERBOSE
select HAVE_DEBUG_KMEMLEAK select HAVE_DEBUG_KMEMLEAK
select HAVE_DMA_CONTIGUOUS select HAVE_DMA_CONTIGUOUS
......
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
#endif #endif
#define __ARCH_WANT_SYS_CLONE #define __ARCH_WANT_SYS_CLONE
#define __ARCH_WANT_SYS_CLONE3
#ifndef __COMPAT_SYSCALL_NR #ifndef __COMPAT_SYSCALL_NR
#include <uapi/asm/unistd.h> #include <uapi/asm/unistd.h>
......
...@@ -19,5 +19,6 @@ ...@@ -19,5 +19,6 @@
#define __ARCH_WANT_NEW_STAT #define __ARCH_WANT_NEW_STAT
#define __ARCH_WANT_SET_GET_RLIMIT #define __ARCH_WANT_SET_GET_RLIMIT
#define __ARCH_WANT_TIME32_SYSCALLS #define __ARCH_WANT_TIME32_SYSCALLS
#define __ARCH_WANT_SYS_CLONE3
#include <asm-generic/unistd.h> #include <asm-generic/unistd.h>
...@@ -360,8 +360,8 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) ...@@ -360,8 +360,8 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
asmlinkage void ret_from_fork(void) asm("ret_from_fork"); asmlinkage void ret_from_fork(void) asm("ret_from_fork");
int copy_thread(unsigned long clone_flags, unsigned long stack_start, int copy_thread_tls(unsigned long clone_flags, unsigned long stack_start,
unsigned long stk_sz, struct task_struct *p) unsigned long stk_sz, struct task_struct *p, unsigned long tls)
{ {
struct pt_regs *childregs = task_pt_regs(p); struct pt_regs *childregs = task_pt_regs(p);
...@@ -394,11 +394,11 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, ...@@ -394,11 +394,11 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
} }
/* /*
* If a TLS pointer was passed to clone (4th argument), use it * If a TLS pointer was passed to clone, use it for the new
* for the new thread. * thread.
*/ */
if (clone_flags & CLONE_SETTLS) if (clone_flags & CLONE_SETTLS)
p->thread.uw.tp_value = childregs->regs[3]; p->thread.uw.tp_value = tls;
} else { } else {
memset(childregs, 0, sizeof(struct pt_regs)); memset(childregs, 0, sizeof(struct pt_regs));
childregs->pstate = PSR_MODE_EL1h; childregs->pstate = PSR_MODE_EL1h;
......
...@@ -62,6 +62,7 @@ config PARISC ...@@ -62,6 +62,7 @@ config PARISC
select HAVE_FTRACE_MCOUNT_RECORD if HAVE_DYNAMIC_FTRACE select HAVE_FTRACE_MCOUNT_RECORD if HAVE_DYNAMIC_FTRACE
select HAVE_KPROBES_ON_FTRACE select HAVE_KPROBES_ON_FTRACE
select HAVE_DYNAMIC_FTRACE_WITH_REGS select HAVE_DYNAMIC_FTRACE_WITH_REGS
select HAVE_COPY_THREAD_TLS
help help
The PA-RISC microprocessor is designed by Hewlett-Packard and used The PA-RISC microprocessor is designed by Hewlett-Packard and used
......
...@@ -208,8 +208,8 @@ arch_initcall(parisc_idle_init); ...@@ -208,8 +208,8 @@ arch_initcall(parisc_idle_init);
* Copy architecture-specific thread state * Copy architecture-specific thread state
*/ */
int int
copy_thread(unsigned long clone_flags, unsigned long usp, copy_thread_tls(unsigned long clone_flags, unsigned long usp,
unsigned long kthread_arg, struct task_struct *p) unsigned long kthread_arg, struct task_struct *p, unsigned long tls)
{ {
struct pt_regs *cregs = &(p->thread.regs); struct pt_regs *cregs = &(p->thread.regs);
void *stack = task_stack_page(p); void *stack = task_stack_page(p);
...@@ -254,9 +254,9 @@ copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -254,9 +254,9 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN + FRAME_SIZE; cregs->ksp = (unsigned long)stack + THREAD_SZ_ALGN + FRAME_SIZE;
cregs->kpc = (unsigned long) &child_return; cregs->kpc = (unsigned long) &child_return;
/* Setup thread TLS area from the 4th parameter in clone */ /* Setup thread TLS area */
if (clone_flags & CLONE_SETTLS) if (clone_flags & CLONE_SETTLS)
cregs->cr27 = cregs->gr[23]; cregs->cr27 = tls;
} }
return 0; return 0;
......
...@@ -65,6 +65,7 @@ config RISCV ...@@ -65,6 +65,7 @@ config RISCV
select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU
select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_MMAP_RND_BITS if MMU
select ARCH_HAS_GCOV_PROFILE_ALL select ARCH_HAS_GCOV_PROFILE_ALL
select HAVE_COPY_THREAD_TLS
config ARCH_MMAP_RND_BITS_MIN config ARCH_MMAP_RND_BITS_MIN
default 18 if 64BIT default 18 if 64BIT
......
...@@ -99,8 +99,8 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) ...@@ -99,8 +99,8 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
return 0; return 0;
} }
int copy_thread(unsigned long clone_flags, unsigned long usp, int copy_thread_tls(unsigned long clone_flags, unsigned long usp,
unsigned long arg, struct task_struct *p) unsigned long arg, struct task_struct *p, unsigned long tls)
{ {
struct pt_regs *childregs = task_pt_regs(p); struct pt_regs *childregs = task_pt_regs(p);
...@@ -121,7 +121,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -121,7 +121,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
if (usp) /* User fork */ if (usp) /* User fork */
childregs->sp = usp; childregs->sp = usp;
if (clone_flags & CLONE_SETTLS) if (clone_flags & CLONE_SETTLS)
childregs->tp = childregs->a5; childregs->tp = tls;
childregs->a0 = 0; /* Return value of fork() */ childregs->a0 = 0; /* Return value of fork() */
p->thread.ra = (unsigned long)ret_from_fork; p->thread.ra = (unsigned long)ret_from_fork;
} }
......
...@@ -14,6 +14,7 @@ config UML ...@@ -14,6 +14,7 @@ config UML
select HAVE_FUTEX_CMPXCHG if FUTEX select HAVE_FUTEX_CMPXCHG if FUTEX
select HAVE_DEBUG_KMEMLEAK select HAVE_DEBUG_KMEMLEAK
select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_BUGVERBOSE
select HAVE_COPY_THREAD_TLS
select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW
select GENERIC_CPU_DEVICES select GENERIC_CPU_DEVICES
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
......
...@@ -36,7 +36,7 @@ extern long subarch_ptrace(struct task_struct *child, long request, ...@@ -36,7 +36,7 @@ extern long subarch_ptrace(struct task_struct *child, long request,
extern unsigned long getreg(struct task_struct *child, int regno); extern unsigned long getreg(struct task_struct *child, int regno);
extern int putreg(struct task_struct *child, int regno, unsigned long value); extern int putreg(struct task_struct *child, int regno, unsigned long value);
extern int arch_copy_tls(struct task_struct *new); extern int arch_set_tls(struct task_struct *new, unsigned long tls);
extern void clear_flushed_tls(struct task_struct *task); extern void clear_flushed_tls(struct task_struct *task);
extern int syscall_trace_enter(struct pt_regs *regs); extern int syscall_trace_enter(struct pt_regs *regs);
extern void syscall_trace_leave(struct pt_regs *regs); extern void syscall_trace_leave(struct pt_regs *regs);
......
...@@ -153,8 +153,8 @@ void fork_handler(void) ...@@ -153,8 +153,8 @@ void fork_handler(void)
userspace(&current->thread.regs.regs, current_thread_info()->aux_fp_regs); userspace(&current->thread.regs.regs, current_thread_info()->aux_fp_regs);
} }
int copy_thread(unsigned long clone_flags, unsigned long sp, int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
unsigned long arg, struct task_struct * p) unsigned long arg, struct task_struct * p, unsigned long tls)
{ {
void (*handler)(void); void (*handler)(void);
int kthread = current->flags & PF_KTHREAD; int kthread = current->flags & PF_KTHREAD;
...@@ -188,7 +188,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp, ...@@ -188,7 +188,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
* Set a new TLS for the child thread? * Set a new TLS for the child thread?
*/ */
if (clone_flags & CLONE_SETTLS) if (clone_flags & CLONE_SETTLS)
ret = arch_copy_tls(p); ret = arch_set_tls(p, tls);
} }
return ret; return ret;
......
...@@ -215,14 +215,12 @@ static int set_tls_entry(struct task_struct* task, struct user_desc *info, ...@@ -215,14 +215,12 @@ static int set_tls_entry(struct task_struct* task, struct user_desc *info,
return 0; return 0;
} }
int arch_copy_tls(struct task_struct *new) int arch_set_tls(struct task_struct *new, unsigned long tls)
{ {
struct user_desc info; struct user_desc info;
int idx, ret = -EFAULT; int idx, ret = -EFAULT;
if (copy_from_user(&info, if (copy_from_user(&info, (void __user *) tls, sizeof(info)))
(void __user *) UPT_SI(&new->thread.regs.regs),
sizeof(info)))
goto out; goto out;
ret = -EINVAL; ret = -EINVAL;
......
...@@ -6,14 +6,13 @@ void clear_flushed_tls(struct task_struct *task) ...@@ -6,14 +6,13 @@ void clear_flushed_tls(struct task_struct *task)
{ {
} }
int arch_copy_tls(struct task_struct *t) int arch_set_tls(struct task_struct *t, unsigned long tls)
{ {
/* /*
* If CLONE_SETTLS is set, we need to save the thread id * If CLONE_SETTLS is set, we need to save the thread id
* (which is argument 5, child_tid, of clone) so it can be set * so it can be set during context switches.
* during context switches.
*/ */
t->thread.arch.fs = t->thread.regs.regs.gp[R8 / sizeof(long)]; t->thread.arch.fs = tls;
return 0; return 0;
} }
...@@ -24,6 +24,7 @@ config XTENSA ...@@ -24,6 +24,7 @@ config XTENSA
select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL
select HAVE_ARCH_KASAN if MMU && !XIP_KERNEL select HAVE_ARCH_KASAN if MMU && !XIP_KERNEL
select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRACEHOOK
select HAVE_COPY_THREAD_TLS
select HAVE_DEBUG_KMEMLEAK select HAVE_DEBUG_KMEMLEAK
select HAVE_DMA_CONTIGUOUS select HAVE_DMA_CONTIGUOUS
select HAVE_EXIT_THREAD select HAVE_EXIT_THREAD
......
...@@ -202,8 +202,9 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) ...@@ -202,8 +202,9 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
* involved. Much simpler to just not copy those live frames across. * involved. Much simpler to just not copy those live frames across.
*/ */
int copy_thread(unsigned long clone_flags, unsigned long usp_thread_fn, int copy_thread_tls(unsigned long clone_flags, unsigned long usp_thread_fn,
unsigned long thread_fn_arg, struct task_struct *p) unsigned long thread_fn_arg, struct task_struct *p,
unsigned long tls)
{ {
struct pt_regs *childregs = task_pt_regs(p); struct pt_regs *childregs = task_pt_regs(p);
...@@ -266,9 +267,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp_thread_fn, ...@@ -266,9 +267,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp_thread_fn,
childregs->syscall = regs->syscall; childregs->syscall = regs->syscall;
/* The thread pointer is passed in the '4th argument' (= a5) */
if (clone_flags & CLONE_SETTLS) if (clone_flags & CLONE_SETTLS)
childregs->threadptr = childregs->areg[5]; childregs->threadptr = tls;
} else { } else {
p->thread.ra = MAKE_RA_FOR_CALL( p->thread.ra = MAKE_RA_FOR_CALL(
(unsigned long)ret_from_kernel_thread, 1); (unsigned long)ret_from_kernel_thread, 1);
......
...@@ -2578,6 +2578,16 @@ SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, ...@@ -2578,6 +2578,16 @@ SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
#endif #endif
#ifdef __ARCH_WANT_SYS_CLONE3 #ifdef __ARCH_WANT_SYS_CLONE3
/*
* copy_thread implementations handle CLONE_SETTLS by reading the TLS value from
* the registers containing the syscall arguments for clone. This doesn't work
* with clone3 since the TLS value is passed in clone_args instead.
*/
#ifndef CONFIG_HAVE_COPY_THREAD_TLS
#error clone3 requires copy_thread_tls support in arch
#endif
noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs, noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs,
struct clone_args __user *uargs, struct clone_args __user *uargs,
size_t usize) size_t usize)
......
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