a8439e2a2ac30d9b05e01ecc08ea72fd2bad9734
[linux-2.6.git] / arch / um / kernel / ptrace.c
1 /* 
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 #ifdef CONFIG_PROC_MM
13 #include "linux/proc_mm.h"
14 #endif
15 #include "asm/ptrace.h"
16 #include "asm/uaccess.h"
17 #include "kern_util.h"
18 #include "ptrace_user.h"
19
20 /*
21  * Called by kernel/ptrace.c when detaching..
22  */
23 void ptrace_disable(struct task_struct *child)
24
25         child->ptrace &= ~PT_DTRACE;
26         child->thread.singlestep_syscall = 0;
27 }
28
29 long sys_ptrace(long request, long pid, long addr, long data)
30 {
31         struct task_struct *child;
32         int i, ret;
33
34         lock_kernel();
35         ret = -EPERM;
36         if (request == PTRACE_TRACEME) {
37                 /* are we already being traced? */
38                 if (current->ptrace & PT_PTRACED)
39                         goto out;
40
41                 ret = security_ptrace(current->parent, current);
42                 if (ret)
43                         goto out;
44
45                 /* set the ptrace bit in the process flags. */
46                 current->ptrace |= PT_PTRACED;
47                 ret = 0;
48                 goto out;
49         }
50         ret = -ESRCH;
51         read_lock(&tasklist_lock);
52         child = find_task_by_pid(pid);
53         if (child)
54                 get_task_struct(child);
55         read_unlock(&tasklist_lock);
56         if (!child)
57                 goto out;
58         if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT))
59                 goto out_tsk;
60
61         ret = -EPERM;
62         if (pid == 1)           /* you may not mess with init */
63                 goto out_tsk;
64
65         if (request == PTRACE_ATTACH) {
66                 ret = ptrace_attach(child);
67                 goto out_tsk;
68         }
69
70         ret = ptrace_check_attach(child, request == PTRACE_KILL);
71         if (ret < 0)
72                 goto out_tsk;
73
74         switch (request) {
75                 /* when I and D space are separate, these will need to be fixed. */
76         case PTRACE_PEEKTEXT: /* read word at location addr. */ 
77         case PTRACE_PEEKDATA: {
78                 unsigned long tmp;
79                 int copied;
80
81                 ret = -EIO;
82                 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
83                 if (copied != sizeof(tmp))
84                         break;
85                 ret = put_user(tmp,(unsigned long *) data);
86                 break;
87         }
88
89         /* read the word at location addr in the USER area. */
90         case PTRACE_PEEKUSR: {
91                 unsigned long tmp;
92
93                 ret = -EIO;
94                 if ((addr & 3) || addr < 0) 
95                         break;
96
97                 tmp = 0;  /* Default return condition */
98                 if(addr < FRAME_SIZE_OFFSET){
99                         tmp = getreg(child, addr);
100                 }
101                 else if((addr >= offsetof(struct user, u_debugreg[0])) &&
102                         (addr <= offsetof(struct user, u_debugreg[7]))){
103                         addr -= offsetof(struct user, u_debugreg[0]);
104                         addr = addr >> 2;
105                         tmp = child->thread.arch.debugregs[addr];
106                 }
107                 ret = put_user(tmp, (unsigned long *) data);
108                 break;
109         }
110
111         /* when I and D space are separate, this will have to be fixed. */
112         case PTRACE_POKETEXT: /* write the word at location addr. */
113         case PTRACE_POKEDATA:
114                 ret = -EIO;
115                 if (access_process_vm(child, addr, &data, sizeof(data), 
116                                       1) != sizeof(data))
117                         break;
118                 ret = 0;
119                 break;
120
121         case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
122                 ret = -EIO;
123                 if ((addr & 3) || addr < 0)
124                         break;
125
126                 if (addr < FRAME_SIZE_OFFSET) {
127                         ret = putreg(child, addr, data);
128                         break;
129                 }
130                 else if((addr >= offsetof(struct user, u_debugreg[0])) &&
131                         (addr <= offsetof(struct user, u_debugreg[7]))){
132                           addr -= offsetof(struct user, u_debugreg[0]);
133                           addr = addr >> 2;
134                           if((addr == 4) || (addr == 5)) break;
135                           child->thread.arch.debugregs[addr] = data;
136                           ret = 0;
137                 }
138
139                 break;
140
141         case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
142         case PTRACE_CONT: { /* restart after signal. */
143                 ret = -EIO;
144                 if ((unsigned long) data > _NSIG)
145                         break;
146
147                 child->ptrace &= ~PT_DTRACE;
148                 child->thread.singlestep_syscall = 0;
149                 if (request == PTRACE_SYSCALL) {
150                         set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
151                 }
152                 else {
153                         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
154                 }
155                 child->exit_code = data;
156                 wake_up_process(child);
157                 ret = 0;
158                 break;
159         }
160
161 /*
162  * make the child exit.  Best I can do is send it a sigkill. 
163  * perhaps it should be put in the status that it wants to 
164  * exit.
165  */
166         case PTRACE_KILL: {
167                 ret = 0;
168                 if (child->exit_state == EXIT_ZOMBIE)   /* already dead */
169                         break;
170
171                 child->ptrace &= ~PT_DTRACE;
172                 child->thread.singlestep_syscall = 0;
173                 child->exit_code = SIGKILL;
174                 wake_up_process(child);
175                 break;
176         }
177
178         case PTRACE_SINGLESTEP: {  /* set the trap flag. */
179                 ret = -EIO;
180                 if ((unsigned long) data > _NSIG)
181                         break;
182                 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
183                 child->ptrace |= PT_DTRACE;
184                 child->thread.singlestep_syscall = 0;
185                 child->exit_code = data;
186                 /* give it a chance to run. */
187                 wake_up_process(child);
188                 ret = 0;
189                 break;
190         }
191
192         case PTRACE_DETACH:
193                 /* detach a process that was attached. */
194                 ret = ptrace_detach(child, data);
195                 break;
196
197 #ifdef PTRACE_GETREGS
198         case PTRACE_GETREGS: { /* Get all gp regs from the child. */
199                 if (!access_ok(VERIFY_WRITE, (unsigned long *)data, 
200                                FRAME_SIZE_OFFSET)) {
201                         ret = -EIO;
202                         break;
203                 }
204                 for ( i = 0; i < FRAME_SIZE_OFFSET; i += sizeof(long) ) {
205                         __put_user(getreg(child, i), (unsigned long *) data);
206                         data += sizeof(long);
207                 }
208                 ret = 0;
209                 break;
210         }
211 #endif
212 #ifdef PTRACE_SETREGS
213         case PTRACE_SETREGS: { /* Set all gp regs in the child. */
214                 unsigned long tmp = 0;
215                 if (!access_ok(VERIFY_READ, (unsigned *)data, 
216                                FRAME_SIZE_OFFSET)) {
217                         ret = -EIO;
218                         break;
219                 }
220                 for ( i = 0; i < FRAME_SIZE_OFFSET; i += sizeof(long) ) {
221                         __get_user(tmp, (unsigned long *) data);
222                         putreg(child, i, tmp);
223                         data += sizeof(long);
224                 }
225                 ret = 0;
226                 break;
227         }
228 #endif
229 #ifdef PTRACE_GETFPREGS
230         case PTRACE_GETFPREGS: /* Get the child FPU state. */
231                 ret = get_fpregs(data, child);
232                 break;
233 #endif
234 #ifdef PTRACE_SETFPREGS
235         case PTRACE_SETFPREGS: /* Set the child FPU state. */
236                 ret = set_fpregs(data, child);
237                 break;
238 #endif
239 #ifdef PTRACE_GETFPXREGS
240         case PTRACE_GETFPXREGS: /* Get the child FPU state. */
241                 ret = get_fpxregs(data, child);
242                 break;
243 #endif
244 #ifdef PTRACE_SETFPXREGS
245         case PTRACE_SETFPXREGS: /* Set the child FPU state. */
246                 ret = set_fpxregs(data, child);
247                 break;
248 #endif
249         case PTRACE_FAULTINFO: {
250                 struct ptrace_faultinfo fault;
251
252                 fault = ((struct ptrace_faultinfo) 
253                         { .is_write     = child->thread.err,
254                           .addr         = child->thread.cr2 });
255                 ret = copy_to_user((unsigned long *) data, &fault, 
256                                    sizeof(fault));
257                 if(ret)
258                         break;
259                 break;
260         }
261         case PTRACE_SIGPENDING:
262                 ret = copy_to_user((unsigned long *) data, 
263                                    &child->pending.signal,
264                                    sizeof(child->pending.signal));
265                 break;
266
267         case PTRACE_LDT: {
268                 struct ptrace_ldt ldt;
269
270                 if(copy_from_user(&ldt, (unsigned long *) data, 
271                                   sizeof(ldt))){
272                         ret = -EIO;
273                         break;
274                 }
275
276                 /* This one is confusing, so just punt and return -EIO for 
277                  * now
278                  */
279                 ret = -EIO;
280                 break;
281         }
282 #ifdef CONFIG_PROC_MM
283         case PTRACE_SWITCH_MM: {
284                 struct mm_struct *old = child->mm;
285                 struct mm_struct *new = proc_mm_get_mm(data);
286
287                 if(IS_ERR(new)){
288                         ret = PTR_ERR(new);
289                         break;
290                 }
291
292                 atomic_inc(&new->mm_users);
293                 child->mm = new;
294                 child->active_mm = new;
295                 mmput(old);
296                 ret = 0;
297                 break;
298         }
299 #endif
300         default:
301                 ret = ptrace_request(child, request, addr, data);
302                 break;
303         }
304  out_tsk:
305         put_task_struct(child);
306  out:
307         unlock_kernel();
308         return ret;
309 }
310
311 void syscall_trace(union uml_pt_regs *regs, int entryexit)
312 {
313         int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit;
314         int tracesysgood;
315
316         if (unlikely(current->audit_context)) {
317                 if (!entryexit)
318                         audit_syscall_entry(current, regs->orig_eax,
319                                             regs->ebx, regs->ecx,
320                                             regs->edx, regs->esi);
321                 else
322                         audit_syscall_exit(current, regs->eax);
323         }
324
325         if (!test_thread_flag(TIF_SYSCALL_TRACE) && !is_singlestep)
326                 return;
327         if (!(current->ptrace & PT_PTRACED))
328                 return;
329
330         /* the 0x80 provides a way for the tracing parent to distinguish
331            between a syscall stop and SIGTRAP delivery */
332         tracesysgood = (current->ptrace & PT_TRACESYSGOOD) && !is_singlestep;
333         ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0));
334
335         /* force do_signal() --> is_syscall() */
336         set_thread_flag(TIF_SIGPENDING);
337
338         /* this isn't the same as continuing with a signal, but it will do
339          * for normal use.  strace only continues with a signal if the
340          * stopping signal is not SIGTRAP.  -brl
341          */
342         if (current->exit_code) {
343                 send_sig(current->exit_code, current, 1);
344                 current->exit_code = 0;
345         }
346 }
347
348 /*
349  * Overrides for Emacs so that we follow Linus's tabbing style.
350  * Emacs will notice this stuff at the end of the file and automatically
351  * adjust the settings for this buffer only.  This must remain at the end
352  * of the file.
353  * ---------------------------------------------------------------------------
354  * Local variables:
355  * c-file-style: "linux"
356  * End:
357  */