Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

um: Rely on PTRACE_SETREGSET to set FS/GS base registers

These registers are saved/restored together with the other general
registers using ptrace. In arch_set_tls we then just need to set the
register and it will be synced back normally.

Most of this logic was introduced in commit f355559cf7845 ("[PATCH] uml:
x86_64 thread fixes"). However, at least today we can rely on ptrace to
restore the base registers for us. As such, only the part of the patch
that tracks the FS register for use as thread local storage is actually
needed.

Signed-off-by: Benjamin Berg <benjamin@sipsolutions.net>
Signed-off-by: Richard Weinberger <richard@nod.at>

authored by

Benjamin Berg and committed by
Richard Weinberger
1ca14435 21822553

+16 -71
-3
arch/um/include/shared/os.h
··· 323 323 extern int __add_sigio_fd(int fd); 324 324 extern int __ignore_sigio_fd(int fd); 325 325 326 - /* prctl.c */ 327 - extern int os_arch_prctl(int pid, int option, unsigned long *arg2); 328 - 329 326 /* tty.c */ 330 327 extern int get_pty(void); 331 328
+2 -2
arch/x86/um/asm/elf.h
··· 168 168 (pr_reg)[18] = (_regs)->regs.gp[18]; \ 169 169 (pr_reg)[19] = (_regs)->regs.gp[19]; \ 170 170 (pr_reg)[20] = (_regs)->regs.gp[20]; \ 171 - (pr_reg)[21] = current->thread.arch.fs; \ 172 - (pr_reg)[22] = 0; \ 171 + (pr_reg)[21] = (_regs)->regs.gp[21]; \ 172 + (pr_reg)[22] = (_regs)->regs.gp[22]; \ 173 173 (pr_reg)[23] = 0; \ 174 174 (pr_reg)[24] = 0; \ 175 175 (pr_reg)[25] = 0; \
-3
arch/x86/um/asm/processor_64.h
··· 10 10 struct arch_thread { 11 11 unsigned long debugregs[8]; 12 12 int debugregs_seq; 13 - unsigned long fs; 14 13 struct faultinfo faultinfo; 15 14 }; 16 15 17 16 #define INIT_ARCH_THREAD { .debugregs = { [ 0 ... 7 ] = 0 }, \ 18 17 .debugregs_seq = 0, \ 19 - .fs = 0, \ 20 18 .faultinfo = { 0, 0, 0 } } 21 19 22 20 #define STACKSLOTS_PER_LINE 4 ··· 26 28 static inline void arch_copy_thread(struct arch_thread *from, 27 29 struct arch_thread *to) 28 30 { 29 - to->fs = from->fs; 30 31 } 31 32 32 33 #define current_sp() ({ void *sp; __asm__("movq %%rsp, %0" : "=r" (sp) : ); sp; })
-1
arch/x86/um/os-Linux/Makefile
··· 6 6 obj-y = registers.o task_size.o mcontext.o 7 7 8 8 obj-$(CONFIG_X86_32) += tls.o 9 - obj-$(CONFIG_64BIT) += prctl.o 10 9 11 10 USER_OBJS := $(obj-y) 12 11
-12
arch/x86/um/os-Linux/prctl.c
··· 1 - /* 2 - * Copyright (C) 2007 Jeff Dike (jdike@{addtoit.com,linux.intel.com}) 3 - * Licensed under the GPL 4 - */ 5 - 6 - #include <sys/ptrace.h> 7 - #include <asm/ptrace.h> 8 - 9 - int os_arch_prctl(int pid, int option, unsigned long *arg2) 10 - { 11 - return ptrace(PTRACE_ARCH_PRCTL, pid, (unsigned long) arg2, option); 12 - }
+13 -49
arch/x86/um/syscalls_64.c
··· 16 16 long arch_prctl(struct task_struct *task, int option, 17 17 unsigned long __user *arg2) 18 18 { 19 - unsigned long *ptr = arg2, tmp; 20 - long ret; 21 - int pid = task->mm->context.id.u.pid; 22 - 23 - /* 24 - * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to 25 - * be safe), we need to call arch_prctl on the host because 26 - * setting %fs may result in something else happening (like a 27 - * GDT or thread.fs being set instead). So, we let the host 28 - * fiddle the registers and thread struct and restore the 29 - * registers afterwards. 30 - * 31 - * So, the saved registers are stored to the process (this 32 - * needed because a stub may have been the last thing to run), 33 - * arch_prctl is run on the host, then the registers are read 34 - * back. 35 - */ 36 - switch (option) { 37 - case ARCH_SET_FS: 38 - case ARCH_SET_GS: 39 - ret = restore_pid_registers(pid, &current->thread.regs.regs); 40 - if (ret) 41 - return ret; 42 - break; 43 - case ARCH_GET_FS: 44 - case ARCH_GET_GS: 45 - /* 46 - * With these two, we read to a local pointer and 47 - * put_user it to the userspace pointer that we were 48 - * given. If addr isn't valid (because it hasn't been 49 - * faulted in or is just bogus), we want put_user to 50 - * fault it in (or return -EFAULT) instead of having 51 - * the host return -EFAULT. 52 - */ 53 - ptr = &tmp; 54 - } 55 - 56 - ret = os_arch_prctl(pid, option, ptr); 57 - if (ret) 58 - return ret; 19 + long ret = -EINVAL; 59 20 60 21 switch (option) { 61 22 case ARCH_SET_FS: 62 - current->thread.arch.fs = (unsigned long) ptr; 63 - ret = save_registers(pid, &current->thread.regs.regs); 23 + current->thread.regs.regs.gp[FS_BASE / sizeof(unsigned long)] = 24 + (unsigned long) arg2; 25 + ret = 0; 64 26 break; 65 27 case ARCH_SET_GS: 66 - ret = save_registers(pid, &current->thread.regs.regs); 28 + current->thread.regs.regs.gp[GS_BASE / sizeof(unsigned long)] = 29 + (unsigned long) arg2; 30 + ret = 0; 67 31 break; 68 32 case ARCH_GET_FS: 69 - ret = put_user(tmp, arg2); 33 + ret = put_user(current->thread.regs.regs.gp[FS_BASE / sizeof(unsigned long)], arg2); 70 34 break; 71 35 case ARCH_GET_GS: 72 - ret = put_user(tmp, arg2); 36 + ret = put_user(current->thread.regs.regs.gp[GS_BASE / sizeof(unsigned long)], arg2); 73 37 break; 74 38 } 75 39 ··· 47 83 48 84 void arch_switch_to(struct task_struct *to) 49 85 { 50 - if ((to->thread.arch.fs == 0) || (to->mm == NULL)) 51 - return; 52 - 53 - arch_prctl(to, ARCH_SET_FS, (void __user *) to->thread.arch.fs); 86 + /* 87 + * Nothing needs to be done on x86_64. 88 + * The FS_BASE/GS_BASE registers are saved in the ptrace register set. 89 + */ 54 90 } 55 91 56 92 SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
+1 -1
arch/x86/um/tls_64.c
··· 12 12 * If CLONE_SETTLS is set, we need to save the thread id 13 13 * so it can be set during context switches. 14 14 */ 15 - t->thread.arch.fs = tls; 15 + t->thread.regs.regs.gp[FS_BASE / sizeof(unsigned long)] = tls; 16 16 17 17 return 0; 18 18 }