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