Import 2.3.40pre5
[davej-history.git] / arch / i386 / kernel / ptrace.c
blobed64f15a2d0de0bae750b30ffa83303005fb4164
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 FS:
75 if (value && (value & 3) != 3)
76 return -EIO;
77 child->thread.fs = value;
78 return 0;
79 case GS:
80 if (value && (value & 3) != 3)
81 return -EIO;
82 child->thread.gs = value;
83 return 0;
84 case DS:
85 case ES:
86 if (value && (value & 3) != 3)
87 return -EIO;
88 value &= 0xffff;
89 break;
90 case SS:
91 case CS:
92 if ((value & 3) != 3)
93 return -EIO;
94 value &= 0xffff;
95 break;
96 case EFL:
97 value &= FLAG_MASK;
98 value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK;
100 if (regno > GS*4)
101 regno -= 2*4;
102 put_stack_long(child, regno - sizeof(struct pt_regs), value);
103 return 0;
106 static unsigned long getreg(struct task_struct *child,
107 unsigned long regno)
109 unsigned long retval = ~0UL;
111 switch (regno >> 2) {
112 case FS:
113 retval = child->thread.fs;
114 break;
115 case GS:
116 retval = child->thread.gs;
117 break;
118 case DS:
119 case ES:
120 case SS:
121 case CS:
122 retval = 0xffff;
123 /* fall through */
124 default:
125 if (regno > GS*4)
126 regno -= 2*4;
127 regno = regno - sizeof(struct pt_regs);
128 retval &= get_stack_long(child, regno);
130 return retval;
133 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
135 struct task_struct *child;
136 struct user * dummy = NULL;
137 unsigned long flags;
138 int i, ret;
140 lock_kernel();
141 ret = -EPERM;
142 if (request == PTRACE_TRACEME) {
143 /* are we already being traced? */
144 if (current->flags & PF_PTRACED)
145 goto out;
146 /* set the ptrace bit in the process flags. */
147 current->flags |= PF_PTRACED;
148 ret = 0;
149 goto out;
151 ret = -ESRCH;
152 read_lock(&tasklist_lock);
153 child = find_task_by_pid(pid);
154 read_unlock(&tasklist_lock); /* FIXME!!! */
155 if (!child)
156 goto out;
157 ret = -EPERM;
158 if (pid == 1) /* you may not mess with init */
159 goto out;
160 if (request == PTRACE_ATTACH) {
161 if (child == current)
162 goto out;
163 if ((!child->dumpable ||
164 (current->uid != child->euid) ||
165 (current->uid != child->suid) ||
166 (current->uid != child->uid) ||
167 (current->gid != child->egid) ||
168 (current->gid != child->sgid) ||
169 (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
170 (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
171 goto out;
172 /* the same process cannot be attached many times */
173 if (child->flags & PF_PTRACED)
174 goto out;
175 child->flags |= PF_PTRACED;
177 write_lock_irqsave(&tasklist_lock, flags);
178 if (child->p_pptr != current) {
179 REMOVE_LINKS(child);
180 child->p_pptr = current;
181 SET_LINKS(child);
183 write_unlock_irqrestore(&tasklist_lock, flags);
185 send_sig(SIGSTOP, child, 1);
186 ret = 0;
187 goto out;
189 ret = -ESRCH;
190 if (!(child->flags & PF_PTRACED))
191 goto out;
192 if (child->state != TASK_STOPPED) {
193 if (request != PTRACE_KILL)
194 goto out;
196 if (child->p_pptr != current)
197 goto out;
199 switch (request) {
200 /* when I and D space are separate, these will need to be fixed. */
201 case PTRACE_PEEKTEXT: /* read word at location addr. */
202 case PTRACE_PEEKDATA: {
203 unsigned long tmp;
204 int copied;
206 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
207 ret = -EIO;
208 if (copied != sizeof(tmp))
209 break;
210 ret = put_user(tmp,(unsigned long *) data);
211 break;
214 /* read the word at location addr in the USER area. */
215 case PTRACE_PEEKUSR: {
216 unsigned long tmp;
218 ret = -EIO;
219 if ((addr & 3) || addr < 0 ||
220 addr > sizeof(struct user) - 3)
221 break;
223 tmp = 0; /* Default return condition */
224 if(addr < 17*sizeof(long))
225 tmp = getreg(child, addr);
226 if(addr >= (long) &dummy->u_debugreg[0] &&
227 addr <= (long) &dummy->u_debugreg[7]){
228 addr -= (long) &dummy->u_debugreg[0];
229 addr = addr >> 2;
230 tmp = child->thread.debugreg[addr];
232 ret = put_user(tmp,(unsigned long *) data);
233 break;
236 /* when I and D space are separate, this will have to be fixed. */
237 case PTRACE_POKETEXT: /* write the word at location addr. */
238 case PTRACE_POKEDATA:
239 ret = 0;
240 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
241 break;
242 ret = -EIO;
243 break;
245 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
246 ret = -EIO;
247 if ((addr & 3) || addr < 0 ||
248 addr > sizeof(struct user) - 3)
249 break;
251 if (addr < 17*sizeof(long)) {
252 ret = putreg(child, addr, data);
253 break;
255 /* We need to be very careful here. We implicitly
256 want to modify a portion of the task_struct, and we
257 have to be selective about what portions we allow someone
258 to modify. */
260 ret = -EIO;
261 if(addr >= (long) &dummy->u_debugreg[0] &&
262 addr <= (long) &dummy->u_debugreg[7]){
264 if(addr == (long) &dummy->u_debugreg[4]) break;
265 if(addr == (long) &dummy->u_debugreg[5]) break;
266 if(addr < (long) &dummy->u_debugreg[4] &&
267 ((unsigned long) data) >= TASK_SIZE-3) break;
269 if(addr == (long) &dummy->u_debugreg[7]) {
270 data &= ~DR_CONTROL_RESERVED;
271 for(i=0; i<4; i++)
272 if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
273 goto out;
276 addr -= (long) &dummy->u_debugreg;
277 addr = addr >> 2;
278 child->thread.debugreg[addr] = data;
279 ret = 0;
281 break;
283 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
284 case PTRACE_CONT: { /* restart after signal. */
285 long tmp;
287 ret = -EIO;
288 if ((unsigned long) data > _NSIG)
289 break;
290 if (request == PTRACE_SYSCALL)
291 child->flags |= PF_TRACESYS;
292 else
293 child->flags &= ~PF_TRACESYS;
294 child->exit_code = data;
295 /* make sure the single step bit is not set. */
296 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
297 put_stack_long(child, EFL_OFFSET,tmp);
298 wake_up_process(child);
299 ret = 0;
300 break;
304 * make the child exit. Best I can do is send it a sigkill.
305 * perhaps it should be put in the status that it wants to
306 * exit.
308 case PTRACE_KILL: {
309 long tmp;
311 ret = 0;
312 if (child->state == TASK_ZOMBIE) /* already dead */
313 break;
314 child->exit_code = SIGKILL;
315 /* make sure the single step bit is not set. */
316 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
317 put_stack_long(child, EFL_OFFSET, tmp);
318 wake_up_process(child);
319 break;
322 case PTRACE_SINGLESTEP: { /* set the trap flag. */
323 long tmp;
325 ret = -EIO;
326 if ((unsigned long) data > _NSIG)
327 break;
328 child->flags &= ~PF_TRACESYS;
329 if ((child->flags & PF_DTRACE) == 0) {
330 /* Spurious delayed TF traps may occur */
331 child->flags |= PF_DTRACE;
333 tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
334 put_stack_long(child, EFL_OFFSET, tmp);
335 child->exit_code = data;
336 /* give it a chance to run. */
337 wake_up_process(child);
338 ret = 0;
339 break;
342 case PTRACE_DETACH: { /* detach a process that was attached. */
343 long tmp;
345 ret = -EIO;
346 if ((unsigned long) data > _NSIG)
347 break;
348 child->flags &= ~(PF_PTRACED|PF_TRACESYS);
349 child->exit_code = data;
350 write_lock_irqsave(&tasklist_lock, flags);
351 REMOVE_LINKS(child);
352 child->p_pptr = child->p_opptr;
353 SET_LINKS(child);
354 write_unlock_irqrestore(&tasklist_lock, flags);
355 /* make sure the single step bit is not set. */
356 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
357 put_stack_long(child, EFL_OFFSET, tmp);
358 wake_up_process(child);
359 ret = 0;
360 break;
363 case PTRACE_GETREGS: { /* Get all gp regs from the child. */
364 if (!access_ok(VERIFY_WRITE, (unsigned *)data, 17*sizeof(long))) {
365 ret = -EIO;
366 break;
368 for ( i = 0; i < 17*sizeof(long); i += sizeof(long) ) {
369 __put_user(getreg(child, i),(unsigned long *) data);
370 data += sizeof(long);
372 ret = 0;
373 break;
376 case PTRACE_SETREGS: { /* Set all gp regs in the child. */
377 unsigned long tmp;
378 if (!access_ok(VERIFY_READ, (unsigned *)data, 17*sizeof(long))) {
379 ret = -EIO;
380 break;
382 for ( i = 0; i < 17*sizeof(long); i += sizeof(long) ) {
383 __get_user(tmp, (unsigned long *) data);
384 putreg(child, i, tmp);
385 data += sizeof(long);
387 ret = 0;
388 break;
391 case PTRACE_GETFPREGS: { /* Get the child FPU state. */
392 if (!access_ok(VERIFY_WRITE, (unsigned *)data, sizeof(struct user_i387_struct))) {
393 ret = -EIO;
394 break;
396 ret = 0;
397 if ( !child->used_math ) {
398 /* Simulate an empty FPU. */
399 child->thread.i387.hard.cwd = 0xffff037f;
400 child->thread.i387.hard.swd = 0xffff0000;
401 child->thread.i387.hard.twd = 0xffffffff;
403 #ifdef CONFIG_MATH_EMULATION
404 if ( boot_cpu_data.hard_math ) {
405 #endif
406 __copy_to_user((void *)data, &child->thread.i387.hard, sizeof(struct user_i387_struct));
407 #ifdef CONFIG_MATH_EMULATION
408 } else {
409 save_i387_soft(&child->thread.i387.soft, (struct _fpstate *)data);
411 #endif
412 break;
415 case PTRACE_SETFPREGS: { /* Set the child FPU state. */
416 if (!access_ok(VERIFY_READ, (unsigned *)data, sizeof(struct user_i387_struct))) {
417 ret = -EIO;
418 break;
420 child->used_math = 1;
421 #ifdef CONFIG_MATH_EMULATION
422 if ( boot_cpu_data.hard_math ) {
423 #endif
424 __copy_from_user(&child->thread.i387.hard, (void *)data, sizeof(struct user_i387_struct));
425 #ifdef CONFIG_MATH_EMULATION
426 } else {
427 restore_i387_soft(&child->thread.i387.soft, (struct _fpstate *)data);
429 #endif
430 ret = 0;
431 break;
434 default:
435 ret = -EIO;
436 break;
438 out:
439 unlock_kernel();
440 return ret;
443 asmlinkage void syscall_trace(void)
445 if ((current->flags & (PF_PTRACED|PF_TRACESYS))
446 != (PF_PTRACED|PF_TRACESYS))
447 return;
448 current->exit_code = SIGTRAP;
449 current->state = TASK_STOPPED;
450 notify_parent(current, SIGCHLD);
451 schedule();
453 * this isn't the same as continuing with a signal, but it will do
454 * for normal use. strace only continues with a signal if the
455 * stopping signal is not SIGTRAP. -brl
457 if (current->exit_code) {
458 send_sig(current->exit_code, current, 1);
459 current->exit_code = 0;