File: /usr/src/linux/arch/i386/kernel/ptrace.c

1     /* ptrace.c */
2     /* By Ross Biro 1/23/92 */
3     /*
4      * Pentium III FXSR, SSE support
5      *	Gareth Hughes <gareth@valinux.com>, May 2000
6      */
7     
8     #include <linux/kernel.h>
9     #include <linux/sched.h>
10     #include <linux/mm.h>
11     #include <linux/smp.h>
12     #include <linux/smp_lock.h>
13     #include <linux/errno.h>
14     #include <linux/ptrace.h>
15     #include <linux/user.h>
16     
17     #include <asm/uaccess.h>
18     #include <asm/pgtable.h>
19     #include <asm/system.h>
20     #include <asm/processor.h>
21     #include <asm/i387.h>
22     #include <asm/debugreg.h>
23     
24     /*
25      * does not yet catch signals sent when the child dies.
26      * in exit.c or in signal.c.
27      */
28     
29     /* determines which flags the user has access to. */
30     /* 1 = access 0 = no access */
31     #define FLAG_MASK 0x00044dd5
32     
33     /* set's the trap flag. */
34     #define TRAP_FLAG 0x100
35     
36     /*
37      * Offset of eflags on child stack..
38      */
39     #define EFL_OFFSET ((EFL-2)*4-sizeof(struct pt_regs))
40     
41     /*
42      * this routine will get a word off of the processes privileged stack. 
43      * the offset is how far from the base addr as stored in the TSS.  
44      * this routine assumes that all the privileged stacks are in our
45      * data space.
46      */   
47     static inline int get_stack_long(struct task_struct *task, int offset)
48     {
49     	unsigned char *stack;
50     
51     	stack = (unsigned char *)task->thread.esp0;
52     	stack += offset;
53     	return (*((int *)stack));
54     }
55     
56     /*
57      * this routine will put a word on the processes privileged stack. 
58      * the offset is how far from the base addr as stored in the TSS.  
59      * this routine assumes that all the privileged stacks are in our
60      * data space.
61      */
62     static inline int put_stack_long(struct task_struct *task, int offset,
63     	unsigned long data)
64     {
65     	unsigned char * stack;
66     
67     	stack = (unsigned char *) task->thread.esp0;
68     	stack += offset;
69     	*(unsigned long *) stack = data;
70     	return 0;
71     }
72     
73     static int putreg(struct task_struct *child,
74     	unsigned long regno, unsigned long value)
75     {
76     	switch (regno >> 2) {
77     		case FS:
78     			if (value && (value & 3) != 3)
79     				return -EIO;
80     			child->thread.fs = value;
81     			return 0;
82     		case GS:
83     			if (value && (value & 3) != 3)
84     				return -EIO;
85     			child->thread.gs = value;
86     			return 0;
87     		case DS:
88     		case ES:
89     			if (value && (value & 3) != 3)
90     				return -EIO;
91     			value &= 0xffff;
92     			break;
93     		case SS:
94     		case CS:
95     			if ((value & 3) != 3)
96     				return -EIO;
97     			value &= 0xffff;
98     			break;
99     		case EFL:
100     			value &= FLAG_MASK;
101     			value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK;
102     			break;
103     	}
104     	if (regno > GS*4)
105     		regno -= 2*4;
106     	put_stack_long(child, regno - sizeof(struct pt_regs), value);
107     	return 0;
108     }
109     
110     static unsigned long getreg(struct task_struct *child,
111     	unsigned long regno)
112     {
113     	unsigned long retval = ~0UL;
114     
115     	switch (regno >> 2) {
116     		case FS:
117     			retval = child->thread.fs;
118     			break;
119     		case GS:
120     			retval = child->thread.gs;
121     			break;
122     		case DS:
123     		case ES:
124     		case SS:
125     		case CS:
126     			retval = 0xffff;
127     			/* fall through */
128     		default:
129     			if (regno > GS*4)
130     				regno -= 2*4;
131     			regno = regno - sizeof(struct pt_regs);
132     			retval &= get_stack_long(child, regno);
133     	}
134     	return retval;
135     }
136     
137     /*
138      * Called by kernel/ptrace.c when detaching..
139      *
140      * Make sure the single step bit is not set.
141      */
142     void ptrace_disable(struct task_struct *child)
143     { 
144     	long tmp;
145     
146     	tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
147     	put_stack_long(child, EFL_OFFSET, tmp);
148     }
149     
150     asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
151     {
152     	struct task_struct *child;
153     	struct user * dummy = NULL;
154     	int i, ret;
155     
156     	lock_kernel();
157     	ret = -EPERM;
158     	if (request == PTRACE_TRACEME) {
159     		/* are we already being traced? */
160     		if (current->ptrace & PT_PTRACED)
161     			goto out;
162     		/* set the ptrace bit in the process flags. */
163     		current->ptrace |= PT_PTRACED;
164     		ret = 0;
165     		goto out;
166     	}
167     	ret = -ESRCH;
168     	read_lock(&tasklist_lock);
169     	child = find_task_by_pid(pid);
170     	if (child)
171     		get_task_struct(child);
172     	read_unlock(&tasklist_lock);
173     	if (!child)
174     		goto out;
175     
176     	ret = -EPERM;
177     	if (pid == 1)		/* you may not mess with init */
178     		goto out_tsk;
179     
180     	if (request == PTRACE_ATTACH) {
181     		ret = ptrace_attach(child);
182     		goto out_tsk;
183     	}
184     	ret = -ESRCH;
185     	if (!(child->ptrace & PT_PTRACED))
186     		goto out_tsk;
187     	if (child->state != TASK_STOPPED) {
188     		if (request != PTRACE_KILL)
189     			goto out_tsk;
190     	}
191     	if (child->p_pptr != current)
192     		goto out_tsk;
193     	switch (request) {
194     	/* when I and D space are separate, these will need to be fixed. */
195     	case PTRACE_PEEKTEXT: /* read word at location addr. */ 
196     	case PTRACE_PEEKDATA: {
197     		unsigned long tmp;
198     		int copied;
199     
200     		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
201     		ret = -EIO;
202     		if (copied != sizeof(tmp))
203     			break;
204     		ret = put_user(tmp,(unsigned long *) data);
205     		break;
206     	}
207     
208     	/* read the word at location addr in the USER area. */
209     	case PTRACE_PEEKUSR: {
210     		unsigned long tmp;
211     
212     		ret = -EIO;
213     		if ((addr & 3) || addr < 0 || 
214     		    addr > sizeof(struct user) - 3)
215     			break;
216     
217     		tmp = 0;  /* Default return condition */
218     		if(addr < FRAME_SIZE*sizeof(long))
219     			tmp = getreg(child, addr);
220     		if(addr >= (long) &dummy->u_debugreg[0] &&
221     		   addr <= (long) &dummy->u_debugreg[7]){
222     			addr -= (long) &dummy->u_debugreg[0];
223     			addr = addr >> 2;
224     			tmp = child->thread.debugreg[addr];
225     		}
226     		ret = put_user(tmp,(unsigned long *) data);
227     		break;
228     	}
229     
230     	/* when I and D space are separate, this will have to be fixed. */
231     	case PTRACE_POKETEXT: /* write the word at location addr. */
232     	case PTRACE_POKEDATA:
233     		ret = 0;
234     		if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
235     			break;
236     		ret = -EIO;
237     		break;
238     
239     	case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
240     		ret = -EIO;
241     		if ((addr & 3) || addr < 0 || 
242     		    addr > sizeof(struct user) - 3)
243     			break;
244     
245     		if (addr < FRAME_SIZE*sizeof(long)) {
246     			ret = putreg(child, addr, data);
247     			break;
248     		}
249     		/* We need to be very careful here.  We implicitly
250     		   want to modify a portion of the task_struct, and we
251     		   have to be selective about what portions we allow someone
252     		   to modify. */
253     
254     		  ret = -EIO;
255     		  if(addr >= (long) &dummy->u_debugreg[0] &&
256     		     addr <= (long) &dummy->u_debugreg[7]){
257     
258     			  if(addr == (long) &dummy->u_debugreg[4]) break;
259     			  if(addr == (long) &dummy->u_debugreg[5]) break;
260     			  if(addr < (long) &dummy->u_debugreg[4] &&
261     			     ((unsigned long) data) >= TASK_SIZE-3) break;
262     			  
263     			  if(addr == (long) &dummy->u_debugreg[7]) {
264     				  data &= ~DR_CONTROL_RESERVED;
265     				  for(i=0; i<4; i++)
266     					  if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
267     						  goto out_tsk;
268     			  }
269     
270     			  addr -= (long) &dummy->u_debugreg;
271     			  addr = addr >> 2;
272     			  child->thread.debugreg[addr] = data;
273     			  ret = 0;
274     		  }
275     		  break;
276     
277     	case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
278     	case PTRACE_CONT: { /* restart after signal. */
279     		long tmp;
280     
281     		ret = -EIO;
282     		if ((unsigned long) data > _NSIG)
283     			break;
284     		if (request == PTRACE_SYSCALL)
285     			child->ptrace |= PT_TRACESYS;
286     		else
287     			child->ptrace &= ~PT_TRACESYS;
288     		child->exit_code = data;
289     	/* make sure the single step bit is not set. */
290     		tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
291     		put_stack_long(child, EFL_OFFSET,tmp);
292     		wake_up_process(child);
293     		ret = 0;
294     		break;
295     	}
296     
297     /*
298      * make the child exit.  Best I can do is send it a sigkill. 
299      * perhaps it should be put in the status that it wants to 
300      * exit.
301      */
302     	case PTRACE_KILL: {
303     		long tmp;
304     
305     		ret = 0;
306     		if (child->state == TASK_ZOMBIE)	/* already dead */
307     			break;
308     		child->exit_code = SIGKILL;
309     		/* make sure the single step bit is not set. */
310     		tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
311     		put_stack_long(child, EFL_OFFSET, tmp);
312     		wake_up_process(child);
313     		break;
314     	}
315     
316     	case PTRACE_SINGLESTEP: {  /* set the trap flag. */
317     		long tmp;
318     
319     		ret = -EIO;
320     		if ((unsigned long) data > _NSIG)
321     			break;
322     		child->ptrace &= ~PT_TRACESYS;
323     		if ((child->ptrace & PT_DTRACE) == 0) {
324     			/* Spurious delayed TF traps may occur */
325     			child->ptrace |= PT_DTRACE;
326     		}
327     		tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
328     		put_stack_long(child, EFL_OFFSET, tmp);
329     		child->exit_code = data;
330     		/* give it a chance to run. */
331     		wake_up_process(child);
332     		ret = 0;
333     		break;
334     	}
335     
336     	case PTRACE_DETACH:
337     		/* detach a process that was attached. */
338     		ret = ptrace_detach(child, data);
339     		break;
340     
341     	case PTRACE_GETREGS: { /* Get all gp regs from the child. */
342     	  	if (!access_ok(VERIFY_WRITE, (unsigned *)data, FRAME_SIZE*sizeof(long))) {
343     			ret = -EIO;
344     			break;
345     		}
346     		for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
347     			__put_user(getreg(child, i),(unsigned long *) data);
348     			data += sizeof(long);
349     		}
350     		ret = 0;
351     		break;
352     	}
353     
354     	case PTRACE_SETREGS: { /* Set all gp regs in the child. */
355     		unsigned long tmp;
356     	  	if (!access_ok(VERIFY_READ, (unsigned *)data, FRAME_SIZE*sizeof(long))) {
357     			ret = -EIO;
358     			break;
359     		}
360     		for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
361     			__get_user(tmp, (unsigned long *) data);
362     			putreg(child, i, tmp);
363     			data += sizeof(long);
364     		}
365     		ret = 0;
366     		break;
367     	}
368     
369     	case PTRACE_GETFPREGS: { /* Get the child FPU state. */
370     		if (!access_ok(VERIFY_WRITE, (unsigned *)data,
371     			       sizeof(struct user_i387_struct))) {
372     			ret = -EIO;
373     			break;
374     		}
375     		ret = 0;
376     		if ( !child->used_math ) {
377     			/* Simulate an empty FPU. */
378     			set_fpu_cwd(child, 0x037f);
379     			set_fpu_swd(child, 0x0000);
380     			set_fpu_twd(child, 0xffff);
381     		}
382     		get_fpregs((struct user_i387_struct *)data, child);
383     		break;
384     	}
385     
386     	case PTRACE_SETFPREGS: { /* Set the child FPU state. */
387     		if (!access_ok(VERIFY_READ, (unsigned *)data,
388     			       sizeof(struct user_i387_struct))) {
389     			ret = -EIO;
390     			break;
391     		}
392     		child->used_math = 1;
393     		set_fpregs(child, (struct user_i387_struct *)data);
394     		ret = 0;
395     		break;
396     	}
397     
398     	case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */
399     		if (!access_ok(VERIFY_WRITE, (unsigned *)data,
400     			       sizeof(struct user_fxsr_struct))) {
401     			ret = -EIO;
402     			break;
403     		}
404     		if ( !child->used_math ) {
405     			/* Simulate an empty FPU. */
406     			set_fpu_cwd(child, 0x037f);
407     			set_fpu_swd(child, 0x0000);
408     			set_fpu_twd(child, 0xffff);
409     			set_fpu_mxcsr(child, 0x1f80);
410     		}
411     		ret = get_fpxregs((struct user_fxsr_struct *)data, child);
412     		break;
413     	}
414     
415     	case PTRACE_SETFPXREGS: { /* Set the child extended FPU state. */
416     		if (!access_ok(VERIFY_READ, (unsigned *)data,
417     			       sizeof(struct user_fxsr_struct))) {
418     			ret = -EIO;
419     			break;
420     		}
421     		child->used_math = 1;
422     		ret = set_fpxregs(child, (struct user_fxsr_struct *)data);
423     		break;
424     	}
425     
426     	case PTRACE_SETOPTIONS: {
427     		if (data & PTRACE_O_TRACESYSGOOD)
428     			child->ptrace |= PT_TRACESYSGOOD;
429     		else
430     			child->ptrace &= ~PT_TRACESYSGOOD;
431     		ret = 0;
432     		break;
433     	}
434     
435     	default:
436     		ret = -EIO;
437     		break;
438     	}
439     out_tsk:
440     	free_task_struct(child);
441     out:
442     	unlock_kernel();
443     	return ret;
444     }
445     
446     asmlinkage void syscall_trace(void)
447     {
448     	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) !=
449     			(PT_PTRACED|PT_TRACESYS))
450     		return;
451     	/* the 0x80 provides a way for the tracing parent to distinguish
452     	   between a syscall stop and SIGTRAP delivery */
453     	current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
454     					? 0x80 : 0);
455     	current->state = TASK_STOPPED;
456     	notify_parent(current, SIGCHLD);
457     	schedule();
458     	/*
459     	 * this isn't the same as continuing with a signal, but it will do
460     	 * for normal use.  strace only continues with a signal if the
461     	 * stopping signal is not SIGTRAP.  -brl
462     	 */
463     	if (current->exit_code) {
464     		send_sig(current->exit_code, current, 1);
465     		current->exit_code = 0;
466     	}
467     }
468