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 <asm/uaccess.h> 11 #include <skas_ptrace.h> 12 13 14 15 void user_enable_single_step(struct task_struct *child) 16 { 17 child->ptrace |= PT_DTRACE; 18 child->thread.singlestep_syscall = 0; 19 20 #ifdef SUBARCH_SET_SINGLESTEPPING 21 SUBARCH_SET_SINGLESTEPPING(child, 1); 22 #endif 23 } 24 25 void user_disable_single_step(struct task_struct *child) 26 { 27 child->ptrace &= ~PT_DTRACE; 28 child->thread.singlestep_syscall = 0; 29 30 #ifdef SUBARCH_SET_SINGLESTEPPING 31 SUBARCH_SET_SINGLESTEPPING(child, 0); 32 #endif 33 } 34 35 /* 36 * Called by kernel/ptrace.c when detaching.. 37 */ 38 void ptrace_disable(struct task_struct *child) 39 { 40 user_disable_single_step(child); 41 } 42 43 extern int peek_user(struct task_struct * child, long addr, long data); 44 extern int poke_user(struct task_struct * child, long addr, long data); 45 46 long arch_ptrace(struct task_struct *child, long request, 47 unsigned long addr, unsigned long data) 48 { 49 int i, ret; 50 unsigned long __user *p = (void __user *)data; 51 void __user *vp = p; 52 53 switch (request) { 54 /* read the word at location addr in the USER area. */ 55 case PTRACE_PEEKUSR: 56 ret = peek_user(child, addr, data); 57 break; 58 59 /* write the word at location addr in the USER area */ 60 case PTRACE_POKEUSR: 61 ret = poke_user(child, addr, data); 62 break; 63 64 case PTRACE_SYSEMU: 65 case PTRACE_SYSEMU_SINGLESTEP: 66 ret = -EIO; 67 break; 68 69 #ifdef PTRACE_GETREGS 70 case PTRACE_GETREGS: { /* Get all gp regs from the child. */ 71 if (!access_ok(VERIFY_WRITE, p, MAX_REG_OFFSET)) { 72 ret = -EIO; 73 break; 74 } 75 for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { 76 __put_user(getreg(child, i), p); 77 p++; 78 } 79 ret = 0; 80 break; 81 } 82 #endif 83 #ifdef PTRACE_SETREGS 84 case PTRACE_SETREGS: { /* Set all gp regs in the child. */ 85 unsigned long tmp = 0; 86 if (!access_ok(VERIFY_READ, p, MAX_REG_OFFSET)) { 87 ret = -EIO; 88 break; 89 } 90 for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { 91 __get_user(tmp, p); 92 putreg(child, i, tmp); 93 p++; 94 } 95 ret = 0; 96 break; 97 } 98 #endif 99 case PTRACE_GET_THREAD_AREA: 100 ret = ptrace_get_thread_area(child, addr, vp); 101 break; 102 103 case PTRACE_SET_THREAD_AREA: 104 ret = ptrace_set_thread_area(child, addr, vp); 105 break; 106 107 case PTRACE_FAULTINFO: { 108 /* 109 * Take the info from thread->arch->faultinfo, 110 * but transfer max. sizeof(struct ptrace_faultinfo). 111 * On i386, ptrace_faultinfo is smaller! 112 */ 113 ret = copy_to_user(p, &child->thread.arch.faultinfo, 114 sizeof(struct ptrace_faultinfo)) ? 115 -EIO : 0; 116 break; 117 } 118 119 #ifdef PTRACE_LDT 120 case PTRACE_LDT: { 121 struct ptrace_ldt ldt; 122 123 if (copy_from_user(&ldt, p, sizeof(ldt))) { 124 ret = -EIO; 125 break; 126 } 127 128 /* 129 * This one is confusing, so just punt and return -EIO for 130 * now 131 */ 132 ret = -EIO; 133 break; 134 } 135 #endif 136 default: 137 ret = ptrace_request(child, request, addr, data); 138 if (ret == -EIO) 139 ret = subarch_ptrace(child, request, addr, data); 140 break; 141 } 142 143 return ret; 144 } 145 146 static void send_sigtrap(struct task_struct *tsk, struct uml_pt_regs *regs, 147 int error_code) 148 { 149 struct siginfo info; 150 151 memset(&info, 0, sizeof(info)); 152 info.si_signo = SIGTRAP; 153 info.si_code = TRAP_BRKPT; 154 155 /* User-mode eip? */ 156 info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL; 157 158 /* Send us the fake SIGTRAP */ 159 force_sig_info(SIGTRAP, &info, tsk); 160 } 161 162 /* 163 * XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and 164 * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check 165 */ 166 void syscall_trace_enter(struct pt_regs *regs) 167 { 168 audit_syscall_entry(UPT_SYSCALL_NR(®s->regs), 169 UPT_SYSCALL_ARG1(®s->regs), 170 UPT_SYSCALL_ARG2(®s->regs), 171 UPT_SYSCALL_ARG3(®s->regs), 172 UPT_SYSCALL_ARG4(®s->regs)); 173 174 if (!test_thread_flag(TIF_SYSCALL_TRACE)) 175 return; 176 177 tracehook_report_syscall_entry(regs); 178 } 179 180 void syscall_trace_leave(struct pt_regs *regs) 181 { 182 int ptraced = current->ptrace; 183 184 audit_syscall_exit(regs); 185 186 /* Fake a debug trap */ 187 if (ptraced & PT_DTRACE) 188 send_sigtrap(current, ®s->regs, 0); 189 190 if (!test_thread_flag(TIF_SYSCALL_TRACE)) 191 return; 192 193 tracehook_report_syscall_exit(regs, 0); 194 /* force do_signal() --> is_syscall() */ 195 if (ptraced & PT_PTRACED) 196 set_thread_flag(TIF_SIGPENDING); 197 } 198