1 /* 2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 3 * Licensed under the GPL 4 */ 5 6 #include <linux/audit.h> 7 #include <linux/ptrace.h> 8 #include <linux/sched.h> 9 #include <linux/tracehook.h> 10 #include <linux/uaccess.h> 11 #include <asm/ptrace-abi.h> 12 13 void user_enable_single_step(struct task_struct *child) 14 { 15 child->ptrace |= PT_DTRACE; 16 child->thread.singlestep_syscall = 0; 17 18 #ifdef SUBARCH_SET_SINGLESTEPPING 19 SUBARCH_SET_SINGLESTEPPING(child, 1); 20 #endif 21 } 22 23 void user_disable_single_step(struct task_struct *child) 24 { 25 child->ptrace &= ~PT_DTRACE; 26 child->thread.singlestep_syscall = 0; 27 28 #ifdef SUBARCH_SET_SINGLESTEPPING 29 SUBARCH_SET_SINGLESTEPPING(child, 0); 30 #endif 31 } 32 33 /* 34 * Called by kernel/ptrace.c when detaching.. 35 */ 36 void ptrace_disable(struct task_struct *child) 37 { 38 user_disable_single_step(child); 39 } 40 41 extern int peek_user(struct task_struct * child, long addr, long data); 42 extern int poke_user(struct task_struct * child, long addr, long data); 43 44 long arch_ptrace(struct task_struct *child, long request, 45 unsigned long addr, unsigned long data) 46 { 47 int i, ret; 48 unsigned long __user *p = (void __user *)data; 49 void __user *vp = p; 50 51 switch (request) { 52 /* read the word at location addr in the USER area. */ 53 case PTRACE_PEEKUSR: 54 ret = peek_user(child, addr, data); 55 break; 56 57 /* write the word at location addr in the USER area */ 58 case PTRACE_POKEUSR: 59 ret = poke_user(child, addr, data); 60 break; 61 62 case PTRACE_SYSEMU: 63 case PTRACE_SYSEMU_SINGLESTEP: 64 ret = -EIO; 65 break; 66 67 #ifdef PTRACE_GETREGS 68 case PTRACE_GETREGS: { /* Get all gp regs from the child. */ 69 if (!access_ok(p, MAX_REG_OFFSET)) { 70 ret = -EIO; 71 break; 72 } 73 for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { 74 __put_user(getreg(child, i), p); 75 p++; 76 } 77 ret = 0; 78 break; 79 } 80 #endif 81 #ifdef PTRACE_SETREGS 82 case PTRACE_SETREGS: { /* Set all gp regs in the child. */ 83 unsigned long tmp = 0; 84 if (!access_ok(p, MAX_REG_OFFSET)) { 85 ret = -EIO; 86 break; 87 } 88 for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { 89 __get_user(tmp, p); 90 putreg(child, i, tmp); 91 p++; 92 } 93 ret = 0; 94 break; 95 } 96 #endif 97 case PTRACE_GET_THREAD_AREA: 98 ret = ptrace_get_thread_area(child, addr, vp); 99 break; 100 101 case PTRACE_SET_THREAD_AREA: 102 ret = ptrace_set_thread_area(child, addr, vp); 103 break; 104 105 default: 106 ret = ptrace_request(child, request, addr, data); 107 if (ret == -EIO) 108 ret = subarch_ptrace(child, request, addr, data); 109 break; 110 } 111 112 return ret; 113 } 114 115 static void send_sigtrap(struct uml_pt_regs *regs, int error_code) 116 { 117 /* Send us the fake SIGTRAP */ 118 force_sig_fault(SIGTRAP, TRAP_BRKPT, 119 /* User-mode eip? */ 120 UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL); 121 } 122 123 /* 124 * XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and 125 * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check 126 */ 127 int syscall_trace_enter(struct pt_regs *regs) 128 { 129 audit_syscall_entry(UPT_SYSCALL_NR(®s->regs), 130 UPT_SYSCALL_ARG1(®s->regs), 131 UPT_SYSCALL_ARG2(®s->regs), 132 UPT_SYSCALL_ARG3(®s->regs), 133 UPT_SYSCALL_ARG4(®s->regs)); 134 135 if (!test_thread_flag(TIF_SYSCALL_TRACE)) 136 return 0; 137 138 return tracehook_report_syscall_entry(regs); 139 } 140 141 void syscall_trace_leave(struct pt_regs *regs) 142 { 143 int ptraced = current->ptrace; 144 145 audit_syscall_exit(regs); 146 147 /* Fake a debug trap */ 148 if (ptraced & PT_DTRACE) 149 send_sigtrap(®s->regs, 0); 150 151 if (!test_thread_flag(TIF_SYSCALL_TRACE)) 152 return; 153 154 tracehook_report_syscall_exit(regs, 0); 155 /* force do_signal() --> is_syscall() */ 156 if (ptraced & PT_PTRACED) 157 set_thread_flag(TIF_SIGPENDING); 158 } 159