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

um: Drop support for hosts without SYSEMU_SINGLESTEP support

These features have existed since Linux 2.6.14 and can be considered
widely available at this point. Also drop the backward compatibility
code for PTRACE_SETOPTIONS.

Signed-off-by: Benjamin Berg <benjamin@sipsolutions.net>

----

v2:
* Continue to define PTRACE_SYSEMU_SINGLESTEP as glibc only added it in
version 2.27.
Signed-off-by: Richard Weinberger <richard@nod.at>

authored by

Benjamin Berg and committed by
Richard Weinberger
a5571984 a8e75902

+25 -244
-1
arch/um/include/asm/processor-generic.h
··· 22 22 struct thread_struct { 23 23 struct pt_regs regs; 24 24 struct pt_regs *segv_regs; 25 - int singlestep_syscall; 26 25 void *fault_addr; 27 26 jmp_buf *fault_catcher; 28 27 struct task_struct *prev_sched;
+1 -2
arch/um/include/shared/kern_util.h
··· 34 34 35 35 extern unsigned int do_IRQ(int irq, struct uml_pt_regs *regs); 36 36 extern void initial_thread_cb(void (*proc)(void *), void *arg); 37 - extern int is_syscall(unsigned long addr); 38 37 39 38 extern void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); 40 39 ··· 57 58 extern unsigned long to_irq_stack(unsigned long *mask_out); 58 59 extern unsigned long from_irq_stack(int nested); 59 60 60 - extern int singlestepping(void *t); 61 + extern int singlestepping(void); 61 62 62 63 extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs); 63 64 extern void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs);
-41
arch/um/include/shared/ptrace_user.h
··· 12 12 extern int ptrace_getregs(long pid, unsigned long *regs_out); 13 13 extern int ptrace_setregs(long pid, unsigned long *regs_in); 14 14 15 - /* syscall emulation path in ptrace */ 16 - 17 - #ifndef PTRACE_SYSEMU 18 - #define PTRACE_SYSEMU 31 19 - #endif 20 - #ifndef PTRACE_SYSEMU_SINGLESTEP 21 - #define PTRACE_SYSEMU_SINGLESTEP 32 22 - #endif 23 - 24 - /* On architectures, that started to support PTRACE_O_TRACESYSGOOD 25 - * in linux 2.4, there are two different definitions of 26 - * PTRACE_SETOPTIONS: linux 2.4 uses 21 while linux 2.6 uses 0x4200. 27 - * For binary compatibility, 2.6 also supports the old "21", named 28 - * PTRACE_OLDSETOPTION. On these architectures, UML always must use 29 - * "21", to ensure the kernel runs on 2.4 and 2.6 host without 30 - * recompilation. So, we use PTRACE_OLDSETOPTIONS in UML. 31 - * We also want to be able to build the kernel on 2.4, which doesn't 32 - * have PTRACE_OLDSETOPTIONS. So, if it is missing, we declare 33 - * PTRACE_OLDSETOPTIONS to be the same as PTRACE_SETOPTIONS. 34 - * 35 - * On architectures, that start to support PTRACE_O_TRACESYSGOOD on 36 - * linux 2.6, PTRACE_OLDSETOPTIONS never is defined, and also isn't 37 - * supported by the host kernel. In that case, our trick lets us use 38 - * the new 0x4200 with the name PTRACE_OLDSETOPTIONS. 39 - */ 40 - #ifndef PTRACE_OLDSETOPTIONS 41 - #define PTRACE_OLDSETOPTIONS PTRACE_SETOPTIONS 42 - #endif 43 - 44 - void set_using_sysemu(int value); 45 - int get_using_sysemu(void); 46 - extern int sysemu_supported; 47 - 48 - #define SELECT_PTRACE_OPERATION(sysemu_mode, singlestep_mode) \ 49 - (((int[3][3] ) { \ 50 - { PTRACE_SYSCALL, PTRACE_SYSCALL, PTRACE_SINGLESTEP }, \ 51 - { PTRACE_SYSEMU, PTRACE_SYSEMU, PTRACE_SINGLESTEP }, \ 52 - { PTRACE_SYSEMU, PTRACE_SYSEMU_SINGLESTEP, \ 53 - PTRACE_SYSEMU_SINGLESTEP } }) \ 54 - [sysemu_mode][singlestep_mode]) 55 - 56 15 #endif
+2 -10
arch/um/kernel/process.c
··· 332 332 333 333 late_initcall(make_proc_sysemu); 334 334 335 - int singlestepping(void * t) 335 + int singlestepping(void) 336 336 { 337 - struct task_struct *task = t ? t : current; 338 - 339 - if (!test_thread_flag(TIF_SINGLESTEP)) 340 - return 0; 341 - 342 - if (task->thread.singlestep_syscall) 343 - return 1; 344 - 345 - return 2; 337 + return test_thread_flag(TIF_SINGLESTEP); 346 338 } 347 339 348 340 /*
-2
arch/um/kernel/ptrace.c
··· 12 12 void user_enable_single_step(struct task_struct *child) 13 13 { 14 14 set_tsk_thread_flag(child, TIF_SINGLESTEP); 15 - child->thread.singlestep_syscall = 0; 16 15 17 16 #ifdef SUBARCH_SET_SINGLESTEPPING 18 17 SUBARCH_SET_SINGLESTEPPING(child, 1); ··· 21 22 void user_disable_single_step(struct task_struct *child) 22 23 { 23 24 clear_tsk_thread_flag(child, TIF_SINGLESTEP); 24 - child->thread.singlestep_syscall = 0; 25 25 26 26 #ifdef SUBARCH_SET_SINGLESTEPPING 27 27 SUBARCH_SET_SINGLESTEPPING(child, 0);
-12
arch/um/kernel/signal.c
··· 121 121 } 122 122 123 123 /* 124 - * This closes a way to execute a system call on the host. If 125 - * you set a breakpoint on a system call instruction and singlestep 126 - * from it, the tracing thread used to PTRACE_SINGLESTEP the process 127 - * rather than PTRACE_SYSCALL it, allowing the system call to execute 128 - * on the host. The tracing thread will check this flag and 129 - * PTRACE_SYSCALL if necessary. 130 - */ 131 - if (test_thread_flag(TIF_SINGLESTEP)) 132 - current->thread.singlestep_syscall = 133 - is_syscall(PT_REGS_IP(&current->thread.regs)); 134 - 135 - /* 136 124 * if there's no signal to deliver, we just put the saved sigmask 137 125 * back 138 126 */
+10 -50
arch/um/os-Linux/skas/process.c
··· 177 177 segv(regs->faultinfo, 0, 1, NULL); 178 178 } 179 179 180 - /* 181 - * To use the same value of using_sysemu as the caller, ask it that value 182 - * (in local_using_sysemu 183 - */ 184 - static void handle_trap(int pid, struct uml_pt_regs *regs, 185 - int local_using_sysemu) 180 + static void handle_trap(int pid, struct uml_pt_regs *regs) 186 181 { 187 - int err, status; 188 - 189 182 if ((UPT_IP(regs) >= STUB_START) && (UPT_IP(regs) < STUB_END)) 190 183 fatal_sigsegv(); 191 - 192 - if (!local_using_sysemu) 193 - { 194 - err = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET, 195 - __NR_getpid); 196 - if (err < 0) { 197 - printk(UM_KERN_ERR "%s - nullifying syscall failed, errno = %d\n", 198 - __func__, errno); 199 - fatal_sigsegv(); 200 - } 201 - 202 - err = ptrace(PTRACE_SYSCALL, pid, 0, 0); 203 - if (err < 0) { 204 - printk(UM_KERN_ERR "%s - continuing to end of syscall failed, errno = %d\n", 205 - __func__, errno); 206 - fatal_sigsegv(); 207 - } 208 - 209 - CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED | __WALL)); 210 - if ((err < 0) || !WIFSTOPPED(status) || 211 - (WSTOPSIG(status) != SIGTRAP + 0x80)) { 212 - err = ptrace_dump_regs(pid); 213 - if (err) 214 - printk(UM_KERN_ERR "Failed to get registers from process, errno = %d\n", 215 - -err); 216 - printk(UM_KERN_ERR "%s - failed to wait at end of syscall, errno = %d, status = %d\n", 217 - __func__, errno, status); 218 - fatal_sigsegv(); 219 - } 220 - } 221 184 222 185 handle_syscall(regs); 223 186 } ··· 318 355 goto out_kill; 319 356 } 320 357 321 - if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL, 358 + if (ptrace(PTRACE_SETOPTIONS, pid, NULL, 322 359 (void *) PTRACE_O_TRACESYSGOOD) < 0) { 323 360 err = -errno; 324 - printk(UM_KERN_ERR "%s : PTRACE_OLDSETOPTIONS failed, errno = %d\n", 361 + printk(UM_KERN_ERR "%s : PTRACE_SETOPTIONS failed, errno = %d\n", 325 362 __func__, errno); 326 363 goto out_kill; 327 364 } ··· 343 380 void userspace(struct uml_pt_regs *regs, unsigned long *aux_fp_regs) 344 381 { 345 382 int err, status, op, pid = userspace_pid[0]; 346 - /* To prevent races if using_sysemu changes under us.*/ 347 - int local_using_sysemu; 348 383 siginfo_t si; 349 384 350 385 /* Handle any immediate reschedules or signals */ ··· 372 411 fatal_sigsegv(); 373 412 } 374 413 375 - /* Now we set local_using_sysemu to be used for one loop */ 376 - local_using_sysemu = get_using_sysemu(); 377 - 378 - op = SELECT_PTRACE_OPERATION(local_using_sysemu, 379 - singlestepping(NULL)); 414 + if (singlestepping()) 415 + op = PTRACE_SYSEMU_SINGLESTEP; 416 + else 417 + op = PTRACE_SYSEMU; 380 418 381 419 if (ptrace(op, pid, 0, 0)) { 382 420 printk(UM_KERN_ERR "%s - ptrace continue failed, op = %d, errno = %d\n", ··· 434 474 else handle_segv(pid, regs, aux_fp_regs); 435 475 break; 436 476 case SIGTRAP + 0x80: 437 - handle_trap(pid, regs, local_using_sysemu); 477 + handle_trap(pid, regs); 438 478 break; 439 479 case SIGTRAP: 440 480 relay_signal(SIGTRAP, (struct siginfo *)&si, regs); ··· 557 597 goto out_kill; 558 598 } 559 599 560 - if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL, 600 + if (ptrace(PTRACE_SETOPTIONS, pid, NULL, 561 601 (void *)PTRACE_O_TRACESYSGOOD) < 0) { 562 602 err = -errno; 563 - printk(UM_KERN_ERR "%s : PTRACE_OLDSETOPTIONS failed, errno = %d\n", 603 + printk(UM_KERN_ERR "%s : PTRACE_SETOPTIONS failed, errno = %d\n", 564 604 __func__, errno); 565 605 goto out_kill; 566 606 }
+6 -64
arch/um/os-Linux/start_up.c
··· 143 143 return ret; 144 144 } 145 145 146 - /* Changed only during early boot */ 147 - static int force_sysemu_disabled = 0; 148 - 149 - static int __init nosysemu_cmd_param(char *str, int* add) 150 - { 151 - force_sysemu_disabled = 1; 152 - return 0; 153 - } 154 - 155 - __uml_setup("nosysemu", nosysemu_cmd_param, 156 - "nosysemu\n" 157 - " Turns off syscall emulation patch for ptrace (SYSEMU).\n" 158 - " SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n" 159 - " behaviour of ptrace() and helps reduce host context switch rates.\n" 160 - " To make it work, you need a kernel patch for your host, too.\n" 161 - " See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n" 162 - " information.\n\n"); 163 - 164 146 static void __init check_sysemu(void) 165 147 { 166 - unsigned long regs[MAX_REG_NR]; 167 148 int pid, n, status, count=0; 168 149 169 - os_info("Checking syscall emulation patch for ptrace..."); 170 - sysemu_supported = 0; 150 + os_info("Checking syscall emulation for ptrace..."); 171 151 pid = start_ptraced_child(); 172 152 173 - if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0) 174 - goto fail; 175 - 176 - CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); 177 - if (n < 0) 178 - fatal_perror("check_sysemu : wait failed"); 179 - if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) 180 - fatal("check_sysemu : expected SIGTRAP, got status = %d\n", 181 - status); 182 - 183 - if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0) 184 - fatal_perror("check_sysemu : PTRACE_GETREGS failed"); 185 - if (PT_SYSCALL_NR(regs) != __NR_getpid) { 186 - non_fatal("check_sysemu got system call number %d, " 187 - "expected %d...", PT_SYSCALL_NR(regs), __NR_getpid); 188 - goto fail; 189 - } 190 - 191 - n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid()); 192 - if (n < 0) { 193 - non_fatal("check_sysemu : failed to modify system call " 194 - "return"); 195 - goto fail; 196 - } 197 - 198 - if (stop_ptraced_child(pid, 0, 0) < 0) 199 - goto fail_stopped; 200 - 201 - sysemu_supported = 1; 202 - os_info("OK\n"); 203 - set_using_sysemu(!force_sysemu_disabled); 204 - 205 - os_info("Checking advanced syscall emulation patch for ptrace..."); 206 - pid = start_ptraced_child(); 207 - 208 - if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0, 153 + if ((ptrace(PTRACE_SETOPTIONS, pid, 0, 209 154 (void *) PTRACE_O_TRACESYSGOOD) < 0)) 210 - fatal_perror("check_sysemu: PTRACE_OLDSETOPTIONS failed"); 155 + fatal_perror("check_sysemu: PTRACE_SETOPTIONS failed"); 211 156 212 157 while (1) { 213 158 count++; ··· 188 243 if (stop_ptraced_child(pid, 0, 0) < 0) 189 244 goto fail_stopped; 190 245 191 - sysemu_supported = 2; 192 246 os_info("OK\n"); 193 247 194 - if (!force_sysemu_disabled) 195 - set_using_sysemu(sysemu_supported); 196 248 return; 197 249 198 250 fail: 199 251 stop_ptraced_child(pid, 1, 0); 200 252 fail_stopped: 201 - non_fatal("missing\n"); 253 + fatal("missing\n"); 202 254 } 203 255 204 256 static void __init check_ptrace(void) ··· 205 263 os_info("Checking that ptrace can change system call numbers..."); 206 264 pid = start_ptraced_child(); 207 265 208 - if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0, 266 + if ((ptrace(PTRACE_SETOPTIONS, pid, 0, 209 267 (void *) PTRACE_O_TRACESYSGOOD) < 0)) 210 - fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed"); 268 + fatal_perror("check_ptrace: PTRACE_SETOPTIONS failed"); 211 269 212 270 while (1) { 213 271 if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
-24
arch/x86/um/ptrace_32.c
··· 25 25 printk(KERN_WARNING "arch_switch_tls failed, errno = EINVAL\n"); 26 26 } 27 27 28 - int is_syscall(unsigned long addr) 29 - { 30 - unsigned short instr; 31 - int n; 32 - 33 - n = copy_from_user(&instr, (void __user *) addr, sizeof(instr)); 34 - if (n) { 35 - /* access_process_vm() grants access to vsyscall and stub, 36 - * while copy_from_user doesn't. Maybe access_process_vm is 37 - * slow, but that doesn't matter, since it will be called only 38 - * in case of singlestepping, if copy_from_user failed. 39 - */ 40 - n = access_process_vm(current, addr, &instr, sizeof(instr), 41 - FOLL_FORCE); 42 - if (n != sizeof(instr)) { 43 - printk(KERN_ERR "is_syscall : failed to read " 44 - "instruction from 0x%lx\n", addr); 45 - return 1; 46 - } 47 - } 48 - /* int 0x80 or sysenter */ 49 - return (instr == 0x80cd) || (instr == 0x340f); 50 - } 51 - 52 28 /* determines which flags the user has access to. */ 53 29 /* 1 = access 0 = no access */ 54 30 #define FLAG_MASK 0x00044dd5
-26
arch/x86/um/ptrace_64.c
··· 188 188 return put_user(tmp, (unsigned long *) data); 189 189 } 190 190 191 - /* XXX Mostly copied from sys-i386 */ 192 - int is_syscall(unsigned long addr) 193 - { 194 - unsigned short instr; 195 - int n; 196 - 197 - n = copy_from_user(&instr, (void __user *) addr, sizeof(instr)); 198 - if (n) { 199 - /* 200 - * access_process_vm() grants access to vsyscall and stub, 201 - * while copy_from_user doesn't. Maybe access_process_vm is 202 - * slow, but that doesn't matter, since it will be called only 203 - * in case of singlestepping, if copy_from_user failed. 204 - */ 205 - n = access_process_vm(current, addr, &instr, sizeof(instr), 206 - FOLL_FORCE); 207 - if (n != sizeof(instr)) { 208 - printk("is_syscall : failed to read instruction from " 209 - "0x%lx\n", addr); 210 - return 1; 211 - } 212 - } 213 - /* sysenter */ 214 - return instr == 0x050f; 215 - } 216 - 217 191 static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child) 218 192 { 219 193 int err, n, cpu = ((struct thread_info *) child->stack)->cpu;
-4
arch/x86/um/shared/sysdep/ptrace_32.h
··· 8 8 9 9 #define MAX_FP_NR HOST_FPX_SIZE 10 10 11 - void set_using_sysemu(int value); 12 - int get_using_sysemu(void); 13 - extern int sysemu_supported; 14 - 15 11 #define UPT_SYSCALL_ARG1(r) UPT_BX(r) 16 12 #define UPT_SYSCALL_ARG2(r) UPT_CX(r) 17 13 #define UPT_SYSCALL_ARG3(r) UPT_DX(r)
+6 -8
arch/x86/um/shared/sysdep/ptrace_user.h
··· 15 15 #define FP_SIZE ((HOST_FPX_SIZE > HOST_FP_SIZE) ? HOST_FPX_SIZE : HOST_FP_SIZE) 16 16 #else 17 17 #define FP_SIZE HOST_FP_SIZE 18 - 19 - /* 20 - * x86_64 FC3 doesn't define this in /usr/include/linux/ptrace.h even though 21 - * it's defined in the kernel's include/linux/ptrace.h. Additionally, use the 22 - * 2.4 name and value for 2.4 host compatibility. 23 - */ 24 - #ifndef PTRACE_OLDSETOPTIONS 25 - #define PTRACE_OLDSETOPTIONS 21 26 18 #endif 27 19 20 + /* 21 + * glibc before 2.27 does not include PTRACE_SYSEMU_SINGLESTEP in its enum, 22 + * ensure we have a definition by (re-)defining it here. 23 + */ 24 + #ifndef PTRACE_SYSEMU_SINGLESTEP 25 + #define PTRACE_SYSEMU_SINGLESTEP 32 28 26 #endif