blob: 60d2eda995c175060615b041abe931e6805faad1 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL
4 */
5
6#include "linux/sched.h"
7#include "linux/mm.h"
8#include "linux/errno.h"
9#include "linux/smp_lock.h"
10#include "linux/security.h"
11#include "linux/ptrace.h"
12#include "linux/audit.h"
13#ifdef CONFIG_PROC_MM
14#include "linux/proc_mm.h"
15#endif
16#include "asm/ptrace.h"
17#include "asm/uaccess.h"
18#include "kern_util.h"
19#include "skas_ptrace.h"
20#include "sysdep/ptrace.h"
21
Bodo Stroesser82c1c112005-05-06 21:30:46 -070022static inline void set_singlestepping(struct task_struct *child, int on)
23{
24 if (on)
25 child->ptrace |= PT_DTRACE;
26 else
27 child->ptrace &= ~PT_DTRACE;
28 child->thread.singlestep_syscall = 0;
29
30#ifdef SUBARCH_SET_SINGLESTEPPING
Jeff Dikeba9950c2005-05-20 13:59:07 -070031 SUBARCH_SET_SINGLESTEPPING(child, on);
Bodo Stroesser82c1c112005-05-06 21:30:46 -070032#endif
Jeff Dikeba9950c2005-05-20 13:59:07 -070033}
Bodo Stroesser82c1c112005-05-06 21:30:46 -070034
Linus Torvalds1da177e2005-04-16 15:20:36 -070035/*
36 * Called by kernel/ptrace.c when detaching..
37 */
38void ptrace_disable(struct task_struct *child)
39{
Bodo Stroesser82c1c112005-05-06 21:30:46 -070040 set_singlestepping(child,0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070041}
42
Bodo Stroesser82c1c112005-05-06 21:30:46 -070043extern int peek_user(struct task_struct * child, long addr, long data);
44extern int poke_user(struct task_struct * child, long addr, long data);
45
Christoph Hellwig481bed42005-11-07 00:59:47 -080046long arch_ptrace(struct task_struct *child, long request, long addr, long data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070047{
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 int i, ret;
Al Viro4d338e12006-03-31 02:30:15 -080049 unsigned long __user *p = (void __user *)(unsigned long)data;
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 switch (request) {
52 /* when I and D space are separate, these will need to be fixed. */
53 case PTRACE_PEEKTEXT: /* read word at location addr. */
54 case PTRACE_PEEKDATA: {
55 unsigned long tmp;
56 int copied;
57
58 ret = -EIO;
59 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
60 if (copied != sizeof(tmp))
61 break;
Al Viro4d338e12006-03-31 02:30:15 -080062 ret = put_user(tmp, p);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 break;
64 }
65
66 /* read the word at location addr in the USER area. */
Bodo Stroesser82c1c112005-05-06 21:30:46 -070067 case PTRACE_PEEKUSR:
68 ret = peek_user(child, addr, data);
69 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
71 /* when I and D space are separate, this will have to be fixed. */
72 case PTRACE_POKETEXT: /* write the word at location addr. */
73 case PTRACE_POKEDATA:
74 ret = -EIO;
75 if (access_process_vm(child, addr, &data, sizeof(data),
76 1) != sizeof(data))
77 break;
78 ret = 0;
79 break;
80
81 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
Bodo Stroesser82c1c112005-05-06 21:30:46 -070082 ret = poke_user(child, addr, data);
83 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
85 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
86 case PTRACE_CONT: { /* restart after signal. */
87 ret = -EIO;
Jesper Juhl7ed20e12005-05-01 08:59:14 -070088 if (!valid_signal(data))
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 break;
90
Bodo Stroesser82c1c112005-05-06 21:30:46 -070091 set_singlestepping(child, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 if (request == PTRACE_SYSCALL) {
93 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
94 }
95 else {
96 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
97 }
98 child->exit_code = data;
99 wake_up_process(child);
100 ret = 0;
101 break;
102 }
103
104/*
105 * make the child exit. Best I can do is send it a sigkill.
106 * perhaps it should be put in the status that it wants to
107 * exit.
108 */
109 case PTRACE_KILL: {
110 ret = 0;
111 if (child->exit_state == EXIT_ZOMBIE) /* already dead */
112 break;
113
Bodo Stroesser82c1c112005-05-06 21:30:46 -0700114 set_singlestepping(child, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 child->exit_code = SIGKILL;
116 wake_up_process(child);
117 break;
118 }
119
120 case PTRACE_SINGLESTEP: { /* set the trap flag. */
121 ret = -EIO;
Jesper Juhl7ed20e12005-05-01 08:59:14 -0700122 if (!valid_signal(data))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 break;
124 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
Bodo Stroesser82c1c112005-05-06 21:30:46 -0700125 set_singlestepping(child, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 child->exit_code = data;
127 /* give it a chance to run. */
128 wake_up_process(child);
129 ret = 0;
130 break;
131 }
132
133 case PTRACE_DETACH:
134 /* detach a process that was attached. */
135 ret = ptrace_detach(child, data);
136 break;
137
138#ifdef PTRACE_GETREGS
139 case PTRACE_GETREGS: { /* Get all gp regs from the child. */
Al Viro4d338e12006-03-31 02:30:15 -0800140 if (!access_ok(VERIFY_WRITE, p, MAX_REG_OFFSET)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 ret = -EIO;
142 break;
143 }
144 for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
Al Viro4d338e12006-03-31 02:30:15 -0800145 __put_user(getreg(child, i), p);
146 p++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 }
148 ret = 0;
149 break;
150 }
151#endif
152#ifdef PTRACE_SETREGS
153 case PTRACE_SETREGS: { /* Set all gp regs in the child. */
154 unsigned long tmp = 0;
Al Viro4d338e12006-03-31 02:30:15 -0800155 if (!access_ok(VERIFY_READ, p, MAX_REG_OFFSET)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 ret = -EIO;
157 break;
158 }
159 for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
Al Viro4d338e12006-03-31 02:30:15 -0800160 __get_user(tmp, p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 putreg(child, i, tmp);
Al Viro4d338e12006-03-31 02:30:15 -0800162 p++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 }
164 ret = 0;
165 break;
166 }
167#endif
168#ifdef PTRACE_GETFPREGS
169 case PTRACE_GETFPREGS: /* Get the child FPU state. */
170 ret = get_fpregs(data, child);
171 break;
172#endif
173#ifdef PTRACE_SETFPREGS
174 case PTRACE_SETFPREGS: /* Set the child FPU state. */
175 ret = set_fpregs(data, child);
176 break;
177#endif
178#ifdef PTRACE_GETFPXREGS
179 case PTRACE_GETFPXREGS: /* Get the child FPU state. */
180 ret = get_fpxregs(data, child);
181 break;
182#endif
183#ifdef PTRACE_SETFPXREGS
184 case PTRACE_SETFPXREGS: /* Set the child FPU state. */
185 ret = set_fpxregs(data, child);
186 break;
187#endif
Paolo 'Blaisorblade' Giarrussoaa6758d2006-03-31 02:30:22 -0800188 case PTRACE_GET_THREAD_AREA:
189 ret = ptrace_get_thread_area(child, addr,
190 (struct user_desc __user *) data);
191 break;
192
193 case PTRACE_SET_THREAD_AREA:
194 ret = ptrace_set_thread_area(child, addr,
195 (struct user_desc __user *) data);
196 break;
197
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 case PTRACE_FAULTINFO: {
Al Viro4d338e12006-03-31 02:30:15 -0800199 /* Take the info from thread->arch->faultinfo,
200 * but transfer max. sizeof(struct ptrace_faultinfo).
201 * On i386, ptrace_faultinfo is smaller!
202 */
203 ret = copy_to_user(p, &child->thread.arch.faultinfo,
204 sizeof(struct ptrace_faultinfo));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 if(ret)
206 break;
207 break;
208 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209
Bodo Stroesserc5784552005-05-05 16:15:31 -0700210#ifdef PTRACE_LDT
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 case PTRACE_LDT: {
212 struct ptrace_ldt ldt;
213
Al Viro4d338e12006-03-31 02:30:15 -0800214 if(copy_from_user(&ldt, p, sizeof(ldt))){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 ret = -EIO;
216 break;
217 }
218
219 /* This one is confusing, so just punt and return -EIO for
220 * now
221 */
222 ret = -EIO;
223 break;
224 }
Bodo Stroesserc5784552005-05-05 16:15:31 -0700225#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226#ifdef CONFIG_PROC_MM
227 case PTRACE_SWITCH_MM: {
228 struct mm_struct *old = child->mm;
229 struct mm_struct *new = proc_mm_get_mm(data);
230
231 if(IS_ERR(new)){
232 ret = PTR_ERR(new);
233 break;
234 }
235
236 atomic_inc(&new->mm_users);
237 child->mm = new;
238 child->active_mm = new;
239 mmput(old);
240 ret = 0;
241 break;
242 }
243#endif
244 default:
245 ret = ptrace_request(child, request, addr, data);
246 break;
247 }
Christoph Hellwig481bed42005-11-07 00:59:47 -0800248
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 return ret;
250}
251
252void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs,
253 int error_code)
254{
255 struct siginfo info;
256
257 memset(&info, 0, sizeof(info));
258 info.si_signo = SIGTRAP;
259 info.si_code = TRAP_BRKPT;
260
261 /* User-mode eip? */
262 info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL;
263
264 /* Send us the fakey SIGTRAP */
265 force_sig_info(SIGTRAP, &info, tsk);
266}
267
268/* XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
269 * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
270 */
271void syscall_trace(union uml_pt_regs *regs, int entryexit)
272{
273 int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit;
274 int tracesysgood;
275
276 if (unlikely(current->audit_context)) {
277 if (!entryexit)
Jeff Dike79d20b12005-05-03 07:54:51 +0100278 audit_syscall_entry(current,
279 HOST_AUDIT_ARCH,
280 UPT_SYSCALL_NR(regs),
281 UPT_SYSCALL_ARG1(regs),
282 UPT_SYSCALL_ARG2(regs),
283 UPT_SYSCALL_ARG3(regs),
284 UPT_SYSCALL_ARG4(regs));
Jeff Dikeb9e0d0692005-05-28 15:51:53 -0700285 else audit_syscall_exit(current,
286 AUDITSC_RESULT(UPT_SYSCALL_RET(regs)),
287 UPT_SYSCALL_RET(regs));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 }
289
290 /* Fake a debug trap */
291 if (is_singlestep)
292 send_sigtrap(current, regs, 0);
293
294 if (!test_thread_flag(TIF_SYSCALL_TRACE))
295 return;
296
297 if (!(current->ptrace & PT_PTRACED))
298 return;
299
300 /* the 0x80 provides a way for the tracing parent to distinguish
301 between a syscall stop and SIGTRAP delivery */
302 tracesysgood = (current->ptrace & PT_TRACESYSGOOD);
303 ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0));
304
305 if (entryexit) /* force do_signal() --> is_syscall() */
306 set_thread_flag(TIF_SIGPENDING);
307
308 /* this isn't the same as continuing with a signal, but it will do
309 * for normal use. strace only continues with a signal if the
310 * stopping signal is not SIGTRAP. -brl
311 */
312 if (current->exit_code) {
313 send_sig(current->exit_code, current, 1);
314 current->exit_code = 0;
315 }
316}