blob: 394582202ce628d62b48933ee4a448e57d20d390 [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
188 case PTRACE_FAULTINFO: {
Al Viro4d338e12006-03-31 02:30:15 -0800189 /* Take the info from thread->arch->faultinfo,
190 * but transfer max. sizeof(struct ptrace_faultinfo).
191 * On i386, ptrace_faultinfo is smaller!
192 */
193 ret = copy_to_user(p, &child->thread.arch.faultinfo,
194 sizeof(struct ptrace_faultinfo));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 if(ret)
196 break;
197 break;
198 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
Bodo Stroesserc5784552005-05-05 16:15:31 -0700200#ifdef PTRACE_LDT
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 case PTRACE_LDT: {
202 struct ptrace_ldt ldt;
203
Al Viro4d338e12006-03-31 02:30:15 -0800204 if(copy_from_user(&ldt, p, sizeof(ldt))){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 ret = -EIO;
206 break;
207 }
208
209 /* This one is confusing, so just punt and return -EIO for
210 * now
211 */
212 ret = -EIO;
213 break;
214 }
Bodo Stroesserc5784552005-05-05 16:15:31 -0700215#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216#ifdef CONFIG_PROC_MM
217 case PTRACE_SWITCH_MM: {
218 struct mm_struct *old = child->mm;
219 struct mm_struct *new = proc_mm_get_mm(data);
220
221 if(IS_ERR(new)){
222 ret = PTR_ERR(new);
223 break;
224 }
225
226 atomic_inc(&new->mm_users);
227 child->mm = new;
228 child->active_mm = new;
229 mmput(old);
230 ret = 0;
231 break;
232 }
233#endif
234 default:
235 ret = ptrace_request(child, request, addr, data);
236 break;
237 }
Christoph Hellwig481bed42005-11-07 00:59:47 -0800238
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 return ret;
240}
241
242void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs,
243 int error_code)
244{
245 struct siginfo info;
246
247 memset(&info, 0, sizeof(info));
248 info.si_signo = SIGTRAP;
249 info.si_code = TRAP_BRKPT;
250
251 /* User-mode eip? */
252 info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL;
253
254 /* Send us the fakey SIGTRAP */
255 force_sig_info(SIGTRAP, &info, tsk);
256}
257
258/* XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
259 * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
260 */
261void syscall_trace(union uml_pt_regs *regs, int entryexit)
262{
263 int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit;
264 int tracesysgood;
265
266 if (unlikely(current->audit_context)) {
267 if (!entryexit)
Jeff Dike79d20b12005-05-03 07:54:51 +0100268 audit_syscall_entry(current,
269 HOST_AUDIT_ARCH,
270 UPT_SYSCALL_NR(regs),
271 UPT_SYSCALL_ARG1(regs),
272 UPT_SYSCALL_ARG2(regs),
273 UPT_SYSCALL_ARG3(regs),
274 UPT_SYSCALL_ARG4(regs));
Jeff Dikeb9e0d062005-05-28 15:51:53 -0700275 else audit_syscall_exit(current,
276 AUDITSC_RESULT(UPT_SYSCALL_RET(regs)),
277 UPT_SYSCALL_RET(regs));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 }
279
280 /* Fake a debug trap */
281 if (is_singlestep)
282 send_sigtrap(current, regs, 0);
283
284 if (!test_thread_flag(TIF_SYSCALL_TRACE))
285 return;
286
287 if (!(current->ptrace & PT_PTRACED))
288 return;
289
290 /* the 0x80 provides a way for the tracing parent to distinguish
291 between a syscall stop and SIGTRAP delivery */
292 tracesysgood = (current->ptrace & PT_TRACESYSGOOD);
293 ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0));
294
295 if (entryexit) /* force do_signal() --> is_syscall() */
296 set_thread_flag(TIF_SIGPENDING);
297
298 /* this isn't the same as continuing with a signal, but it will do
299 * for normal use. strace only continues with a signal if the
300 * stopping signal is not SIGTRAP. -brl
301 */
302 if (current->exit_code) {
303 send_sig(current->exit_code, current, 1);
304 current->exit_code = 0;
305 }
306}