Import 2.3.11pre3
[davej-history.git] / arch / i386 / kernel / ptrace.c
blobe8645129123b67a6eec2fa5676dc1e2b73b37172
1 /* ptrace.c */
2 /* By Ross Biro 1/23/92 */
3 /* edited by Linus Torvalds */
5 #include <linux/config.h> /* for CONFIG_MATH_EMULATION */
6 #include <linux/kernel.h>
7 #include <linux/sched.h>
8 #include <linux/mm.h>
9 #include <linux/smp.h>
10 #include <linux/smp_lock.h>
11 #include <linux/errno.h>
12 #include <linux/ptrace.h>
13 #include <linux/user.h>
15 #include <asm/uaccess.h>
16 #include <asm/pgtable.h>
17 #include <asm/system.h>
18 #include <asm/processor.h>
19 #include <asm/debugreg.h>
22 * does not yet catch signals sent when the child dies.
23 * in exit.c or in signal.c.
26 /* determines which flags the user has access to. */
27 /* 1 = access 0 = no access */
28 #define FLAG_MASK 0x00044dd5
30 /* set's the trap flag. */
31 #define TRAP_FLAG 0x100
34 * Offset of eflags on child stack..
36 #define EFL_OFFSET ((EFL-2)*4-sizeof(struct pt_regs))
39 * this routine will get a word off of the processes privileged stack.
40 * the offset is how far from the base addr as stored in the TSS.
41 * this routine assumes that all the privileged stacks are in our
42 * data space.
43 */
44 static inline int get_stack_long(struct task_struct *task, int offset)
46 unsigned char *stack;
48 stack = (unsigned char *)task->thread.esp0;
49 stack += offset;
50 return (*((int *)stack));
54 * this routine will put a word on the processes privileged stack.
55 * the offset is how far from the base addr as stored in the TSS.
56 * this routine assumes that all the privileged stacks are in our
57 * data space.
59 static inline int put_stack_long(struct task_struct *task, int offset,
60 unsigned long data)
62 unsigned char * stack;
64 stack = (unsigned char *) task->thread.esp0;
65 stack += offset;
66 *(unsigned long *) stack = data;
67 return 0;
70 static int putreg(struct task_struct *child,
71 unsigned long regno, unsigned long value)
73 switch (regno >> 2) {
74 case ORIG_EAX:
75 return -EIO;
76 case FS:
77 if (value && (value & 3) != 3)
78 return -EIO;
79 child->thread.fs = value;
80 return 0;
81 case GS:
82 if (value && (value & 3) != 3)
83 return -EIO;
84 child->thread.gs = value;
85 return 0;
86 case DS:
87 case ES:
88 if (value && (value & 3) != 3)
89 return -EIO;
90 value &= 0xffff;
91 break;
92 case SS:
93 case CS:
94 if ((value & 3) != 3)
95 return -EIO;
96 value &= 0xffff;
97 break;
98 case EFL:
99 value &= FLAG_MASK;
100 value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK;
102 if (regno > GS*4)
103 regno -= 2*4;
104 put_stack_long(child, regno - sizeof(struct pt_regs), value);
105 return 0;
108 static unsigned long getreg(struct task_struct *child,
109 unsigned long regno)
111 unsigned long retval = ~0UL;
113 switch (regno >> 2) {
114 case FS:
115 retval = child->thread.fs;
116 break;
117 case GS:
118 retval = child->thread.gs;
119 break;
120 case DS:
121 case ES:
122 case SS:
123 case CS:
124 retval = 0xffff;
125 /* fall through */
126 default:
127 if (regno > GS*4)
128 regno -= 2*4;
129 regno = regno - sizeof(struct pt_regs);
130 retval &= get_stack_long(child, regno);
132 return retval;
135 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
137 struct task_struct *child;
138 struct user * dummy = NULL;
139 unsigned long flags;
140 int i, ret;
142 lock_kernel();
143 ret = -EPERM;
144 if (request == PTRACE_TRACEME) {
145 /* are we already being traced? */
146 if (current->flags & PF_PTRACED)
147 goto out;
148 /* set the ptrace bit in the process flags. */
149 current->flags |= PF_PTRACED;
150 ret = 0;
151 goto out;
153 ret = -ESRCH;
154 read_lock(&tasklist_lock);
155 child = find_task_by_pid(pid);
156 read_unlock(&tasklist_lock); /* FIXME!!! */
157 if (!child)
158 goto out;
159 ret = -EPERM;
160 if (pid == 1) /* you may not mess with init */
161 goto out;
162 if (request == PTRACE_ATTACH) {
163 if (child == current)
164 goto out;
165 if ((!child->dumpable ||
166 (current->uid != child->euid) ||
167 (current->uid != child->suid) ||
168 (current->uid != child->uid) ||
169 (current->gid != child->egid) ||
170 (current->gid != child->sgid) ||
171 (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
172 (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
173 goto out;
174 /* the same process cannot be attached many times */
175 if (child->flags & PF_PTRACED)
176 goto out;
177 child->flags |= PF_PTRACED;
179 write_lock_irqsave(&tasklist_lock, flags);
180 if (child->p_pptr != current) {
181 REMOVE_LINKS(child);
182 child->p_pptr = current;
183 SET_LINKS(child);
185 write_unlock_irqrestore(&tasklist_lock, flags);
187 send_sig(SIGSTOP, child, 1);
188 ret = 0;
189 goto out;
191 ret = -ESRCH;
192 if (!(child->flags & PF_PTRACED))
193 goto out;
194 if (child->state != TASK_STOPPED) {
195 if (request != PTRACE_KILL)
196 goto out;
198 if (child->p_pptr != current)
199 goto out;
201 switch (request) {
202 /* when I and D space are separate, these will need to be fixed. */
203 case PTRACE_PEEKTEXT: /* read word at location addr. */
204 case PTRACE_PEEKDATA: {
205 unsigned long tmp;
206 int copied;
208 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
209 ret = -EIO;
210 if (copied != sizeof(tmp))
211 goto out;
212 ret = put_user(tmp,(unsigned long *) data);
213 goto out;
216 /* read the word at location addr in the USER area. */
217 case PTRACE_PEEKUSR: {
218 unsigned long tmp;
220 ret = -EIO;
221 if ((addr & 3) || addr < 0 ||
222 addr > sizeof(struct user) - 3)
223 goto out;
225 tmp = 0; /* Default return condition */
226 if(addr < 17*sizeof(long))
227 tmp = getreg(child, addr);
228 if(addr >= (long) &dummy->u_debugreg[0] &&
229 addr <= (long) &dummy->u_debugreg[7]){
230 addr -= (long) &dummy->u_debugreg[0];
231 addr = addr >> 2;
232 tmp = child->thread.debugreg[addr];
234 ret = put_user(tmp,(unsigned long *) data);
235 goto out;
238 /* when I and D space are separate, this will have to be fixed. */
239 case PTRACE_POKETEXT: /* write the word at location addr. */
240 case PTRACE_POKEDATA:
241 ret = 0;
242 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
243 goto out;
244 ret = -EIO;
245 goto out;
247 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
248 ret = -EIO;
249 if ((addr & 3) || addr < 0 ||
250 addr > sizeof(struct user) - 3)
251 goto out;
253 if (addr < 17*sizeof(long)) {
254 ret = putreg(child, addr, data);
255 goto out;
258 /* We need to be very careful here. We implicitly
259 want to modify a portion of the task_struct, and we
260 have to be selective about what portions we allow someone
261 to modify. */
263 if(addr >= (long) &dummy->u_debugreg[0] &&
264 addr <= (long) &dummy->u_debugreg[7]){
266 if(addr == (long) &dummy->u_debugreg[4]) return -EIO;
267 if(addr == (long) &dummy->u_debugreg[5]) return -EIO;
268 if(addr < (long) &dummy->u_debugreg[4] &&
269 ((unsigned long) data) >= TASK_SIZE-3) return -EIO;
271 ret = -EIO;
272 if(addr == (long) &dummy->u_debugreg[7]) {
273 data &= ~DR_CONTROL_RESERVED;
274 for(i=0; i<4; i++)
275 if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
276 goto out;
279 addr -= (long) &dummy->u_debugreg;
280 addr = addr >> 2;
281 child->thread.debugreg[addr] = data;
282 ret = 0;
283 goto out;
285 ret = -EIO;
286 goto out;
288 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
289 case PTRACE_CONT: { /* restart after signal. */
290 long tmp;
292 ret = -EIO;
293 if ((unsigned long) data > _NSIG)
294 goto out;
295 if (request == PTRACE_SYSCALL)
296 child->flags |= PF_TRACESYS;
297 else
298 child->flags &= ~PF_TRACESYS;
299 child->exit_code = data;
300 /* make sure the single step bit is not set. */
301 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
302 put_stack_long(child, EFL_OFFSET,tmp);
303 wake_up_process(child);
304 ret = 0;
305 goto out;
309 * make the child exit. Best I can do is send it a sigkill.
310 * perhaps it should be put in the status that it wants to
311 * exit.
313 case PTRACE_KILL: {
314 long tmp;
316 ret = 0;
317 if (child->state == TASK_ZOMBIE) /* already dead */
318 goto out;
319 child->exit_code = SIGKILL;
320 /* make sure the single step bit is not set. */
321 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
322 put_stack_long(child, EFL_OFFSET, tmp);
323 wake_up_process(child);
324 goto out;
327 case PTRACE_SINGLESTEP: { /* set the trap flag. */
328 long tmp;
330 ret = -EIO;
331 if ((unsigned long) data > _NSIG)
332 goto out;
333 child->flags &= ~PF_TRACESYS;
334 if ((child->flags & PF_DTRACE) == 0) {
335 /* Spurious delayed TF traps may occur */
336 child->flags |= PF_DTRACE;
338 tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
339 put_stack_long(child, EFL_OFFSET, tmp);
340 child->exit_code = data;
341 /* give it a chance to run. */
342 wake_up_process(child);
343 ret = 0;
344 goto out;
347 case PTRACE_DETACH: { /* detach a process that was attached. */
348 long tmp;
350 ret = -EIO;
351 if ((unsigned long) data > _NSIG)
352 goto out;
353 child->flags &= ~(PF_PTRACED|PF_TRACESYS);
354 child->exit_code = data;
355 write_lock_irqsave(&tasklist_lock, flags);
356 REMOVE_LINKS(child);
357 child->p_pptr = child->p_opptr;
358 SET_LINKS(child);
359 write_unlock_irqrestore(&tasklist_lock, flags);
360 /* make sure the single step bit is not set. */
361 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
362 put_stack_long(child, EFL_OFFSET, tmp);
363 wake_up_process(child);
364 ret = 0;
365 goto out;
368 case PTRACE_GETREGS: { /* Get all gp regs from the child. */
369 if (!access_ok(VERIFY_WRITE, (unsigned *)data,
370 17*sizeof(long)))
372 ret = -EIO;
373 goto out;
375 for ( i = 0; i < 17*sizeof(long); i += sizeof(long) )
377 __put_user(getreg(child, i),(unsigned long *) data);
378 data += sizeof(long);
380 ret = 0;
381 goto out;
384 case PTRACE_SETREGS: { /* Set all gp regs in the child. */
385 unsigned long tmp;
386 if (!access_ok(VERIFY_READ, (unsigned *)data,
387 17*sizeof(long)))
389 ret = -EIO;
390 goto out;
392 for ( i = 0; i < 17*sizeof(long); i += sizeof(long) )
394 __get_user(tmp, (unsigned long *) data);
395 putreg(child, i, tmp);
396 data += sizeof(long);
398 ret = 0;
399 goto out;
402 case PTRACE_GETFPREGS: { /* Get the child FPU state. */
403 if (!access_ok(VERIFY_WRITE, (unsigned *)data,
404 sizeof(struct user_i387_struct)))
406 ret = -EIO;
407 goto out;
409 ret = 0;
410 if ( !child->used_math ) {
411 /* Simulate an empty FPU. */
412 child->thread.i387.hard.cwd = 0xffff037f;
413 child->thread.i387.hard.swd = 0xffff0000;
414 child->thread.i387.hard.twd = 0xffffffff;
416 #ifdef CONFIG_MATH_EMULATION
417 if ( boot_cpu_data.hard_math ) {
418 #endif
419 __copy_to_user((void *)data, &child->thread.i387.hard,
420 sizeof(struct user_i387_struct));
421 #ifdef CONFIG_MATH_EMULATION
422 } else {
423 save_i387_soft(&child->thread.i387.soft,
424 (struct _fpstate *)data);
426 #endif
427 goto out;
430 case PTRACE_SETFPREGS: { /* Set the child FPU state. */
431 if (!access_ok(VERIFY_READ, (unsigned *)data,
432 sizeof(struct user_i387_struct)))
434 ret = -EIO;
435 goto out;
437 child->used_math = 1;
438 #ifdef CONFIG_MATH_EMULATION
439 if ( boot_cpu_data.hard_math ) {
440 #endif
441 __copy_from_user(&child->thread.i387.hard, (void *)data,
442 sizeof(struct user_i387_struct));
443 #ifdef CONFIG_MATH_EMULATION
444 } else {
445 restore_i387_soft(&child->thread.i387.soft,
446 (struct _fpstate *)data);
448 #endif
449 ret = 0;
450 goto out;
453 default:
454 ret = -EIO;
455 goto out;
457 out:
458 unlock_kernel();
459 return ret;
462 asmlinkage void syscall_trace(void)
464 if ((current->flags & (PF_PTRACED|PF_TRACESYS))
465 != (PF_PTRACED|PF_TRACESYS))
466 return;
467 current->exit_code = SIGTRAP;
468 current->state = TASK_STOPPED;
469 notify_parent(current, SIGCHLD);
470 schedule();
472 * this isn't the same as continuing with a signal, but it will do
473 * for normal use. strace only continues with a signal if the
474 * stopping signal is not SIGTRAP. -brl
476 if (current->exit_code) {
477 send_sig(current->exit_code, current, 1);
478 current->exit_code = 0;