1 /* ptrace.c */
2 /* By Ross Biro 1/23/92 */
3 /* edited by Linus Torvalds */
4
5 #include <linux/head.h>
6 #include <linux/kernel.h>
7 #include <linux/sched.h>
8 #include <linux/mm.h>
9 #include <linux/errno.h>
10 #include <linux/ptrace.h>
11
12 #include <asm/segment.h>
13 #include <asm/system.h>
14
15 /*
16 * does not yet catch signals sent when the child dies.
17 * in exit.c or in signal.c.
18 */
19
20 /* determines which flags the user has access to. */
21 /* 1 = access 0 = no access */
22 #define FLAG_MASK 0x00000dd9
23
24 /* set's the trap flag. */
25 #define TRAP_FLAG 0x100
26
27 /*
28 * this is the number to subtract from the top of the stack. To find
29 * the local frame.
30 */
31 #define MAGICNUMBER 68
32
33 /* change a pid into a task struct. */
34 static inline struct task_struct * get_task(int pid)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
35 {
36 int i;
37
38 for (i = 1; i < NR_TASKS; i++) {
39 if (task[i] != NULL && (task[i]->pid == pid))
40 return task[i];
41 }
42 return NULL;
43 }
44
45 /*
46 * this routine will get a word off of the processes priviledged stack.
47 * the offset is how far from the base addr as stored in the TSS.
48 * this routine assumes that all the priviledged stacks are in our
49 * data space.
50 */
51 static inline int get_stack_long(struct task_struct *task, int offset)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
52 {
53 unsigned char *stack;
54
55 stack = (unsigned char *)task->tss.esp0;
56 stack += offset;
57 return (*((int *)stack));
58 }
59
60 /*
61 * this routine will put a word on the processes priviledged stack.
62 * the offset is how far from the base addr as stored in the TSS.
63 * this routine assumes that all the priviledged stacks are in our
64 * data space.
65 */
66 static inline int put_stack_long(struct task_struct *task, int offset,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
67 unsigned long data)
68 {
69 unsigned char * stack;
70
71 stack = (unsigned char *) task->tss.esp0;
72 stack += offset;
73 *(unsigned long *) stack = data;
74 return 0;
75 }
76
77 /*
78 * This routine gets a long from any process space by following the page
79 * tables. NOTE! You should check that the long isn't on a page boundary,
80 * and that it is in the task area before calling this: this routine does
81 * no checking.
82 *
83 * NOTE2! This uses "tsk->tss.cr3" even though we know it's currently always
84 * zero. This routine shouldn't have to change when we make a better mm.
85 */
86 static unsigned long get_long(struct task_struct * tsk,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
87 unsigned long addr)
88 {
89 unsigned long page;
90
91 repeat:
92 page = tsk->tss.cr3 + ((addr >> 20) & 0xffc);
93 page = *(unsigned long *) page;
94 if (page & PAGE_PRESENT) {
95 page &= 0xfffff000;
96 page += (addr >> 10) & 0xffc;
97 page = *((unsigned long *) page);
98 }
99 if (!(page & PAGE_PRESENT)) {
100 do_no_page(0,addr,tsk,0);
101 goto repeat;
102 }
103 page &= 0xfffff000;
104 page += addr & 0xfff;
105 return *(unsigned long *) page;
106 }
107
108 /*
109 * This routine puts a long into any process space by following the page
110 * tables. NOTE! You should check that the long isn't on a page boundary,
111 * and that it is in the task area before calling this: this routine does
112 * no checking.
113 */
114 static void put_long(struct task_struct * tsk, unsigned long addr,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
115 unsigned long data)
116 {
117 unsigned long page;
118
119 repeat:
120 page = tsk->tss.cr3 + ((addr >> 20) & 0xffc);
121 page = *(unsigned long *) page;
122 if (page & PAGE_PRESENT) {
123 page &= 0xfffff000;
124 page += (addr >> 10) & 0xffc;
125 /* we're bypassing pagetables, so we have to set the dirty bit ourselves */
126 *(unsigned long *) page |= PAGE_DIRTY;
127 page = *((unsigned long *) page);
128 }
129 if (!(page & PAGE_PRESENT)) {
130 do_no_page(0,addr,tsk,0);
131 goto repeat;
132 }
133 if (!(page & PAGE_RW)) {
134 do_wp_page(0,addr,tsk,0);
135 goto repeat;
136 }
137 page &= 0xfffff000;
138 page += addr & 0xfff;
139 *(unsigned long *) page = data;
140 }
141
142 /*
143 * This routine checks the page boundaries, and that the offset is
144 * within the task area. It then calls get_long() to read a long.
145 */
146 static int read_long(struct task_struct * tsk, unsigned long addr,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
147 unsigned long * result)
148 {
149 unsigned long low,high;
150
151 if (addr > TASK_SIZE-4)
152 return -EIO;
153 if ((addr & 0xfff) > PAGE_SIZE-4) {
154 low = get_long(tsk,addr & 0xfffffffc);
155 high = get_long(tsk,(addr+4) & 0xfffffffc);
156 switch (addr & 3) {
157 case 1:
158 low >>= 8;
159 low |= high << 24;
160 break;
161 case 2:
162 low >>= 16;
163 low |= high << 16;
164 break;
165 case 3:
166 low >>= 24;
167 low |= high << 8;
168 break;
169 }
170 *result = low;
171 } else
172 *result = get_long(tsk,addr);
173 return 0;
174 }
175
176 /*
177 * This routine checks the page boundaries, and that the offset is
178 * within the task area. It then calls put_long() to write a long.
179 */
180 static int write_long(struct task_struct * tsk, unsigned long addr,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
181 unsigned long data)
182 {
183 unsigned long low,high;
184
185 if (addr > TASK_SIZE-4)
186 return -EIO;
187 if ((addr & 0xfff) > PAGE_SIZE-4) {
188 low = get_long(tsk,addr & 0xfffffffc);
189 high = get_long(tsk,(addr+4) & 0xfffffffc);
190 switch (addr & 3) {
191 case 0: /* shouldn't happen, but safety first */
192 low = data;
193 break;
194 case 1:
195 low &= 0x000000ff;
196 low |= data << 8;
197 high &= 0xffffff00;
198 high |= data >> 24;
199 break;
200 case 2:
201 low &= 0x0000ffff;
202 low |= data << 16;
203 high &= 0xffff0000;
204 high |= data >> 16;
205 break;
206 case 3:
207 low &= 0x00ffffff;
208 low |= data << 24;
209 high &= 0xff000000;
210 high |= data >> 8;
211 break;
212 }
213 put_long(tsk,addr & 0xfffffffc,low);
214 put_long(tsk,(addr+4) & 0xfffffffc,high);
215 } else
216 put_long(tsk,addr,data);
217 return 0;
218 }
219
220 int sys_ptrace(long request, long pid, long addr, long data)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
221 {
222 struct task_struct *child;
223
224 if (request == PTRACE_TRACEME) {
225 /* are we already being traced? */
226 if (current->flags & PF_PTRACED)
227 return -EPERM;
228 /* set the ptrace bit in the proccess flags. */
229 current->flags |= PF_PTRACED;
230 return 0;
231 }
232 if (pid == 1) /* you may not mess with init */
233 return -EPERM;
234 if (!(child = get_task(pid)))
235 return -ESRCH;
236 if (request == PTRACE_ATTACH) {
237 long tmp;
238
239 if (child == current)
240 return -EPERM;
241 if ((!child->dumpable || (current->uid != child->euid) ||
242 (current->gid != child->egid)) && !suser())
243 return -EPERM;
244 /* the same process cannot be attached many times */
245 if (child->flags & PF_PTRACED)
246 return -EPERM;
247 child->flags |= PF_PTRACED;
248 if (child->p_pptr != current) {
249 REMOVE_LINKS(child);
250 child->p_pptr = current;
251 SET_LINKS(child);
252 }
253 tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) | TRAP_FLAG;
254 put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
255 if (child->state == TASK_INTERRUPTIBLE ||
256 child->state == TASK_STOPPED)
257 child->state = TASK_RUNNING;
258 child->signal = 0;
259 return 0;
260 }
261 if (!(child->flags & PF_PTRACED))
262 return -ESRCH;
263 if (child->state != TASK_STOPPED && request != PTRACE_DETACH)
264 return -ESRCH;
265 if (child->p_pptr != current)
266 return -ESRCH;
267
268 switch (request) {
269 /* when I and D space are seperate, these will need to be fixed. */
270 case PTRACE_PEEKTEXT: /* read word at location addr. */
271 case PTRACE_PEEKDATA: {
272 int tmp,res;
273
274 res = read_long(child, addr, &tmp);
275 if (res < 0)
276 return res;
277 res = verify_area(VERIFY_WRITE, (void *) data, 4);
278 if (!res)
279 put_fs_long(tmp,(unsigned long *) data);
280 return res;
281 }
282
283 /* read the word at location addr in the USER area. */
284 case PTRACE_PEEKUSR: {
285 int tmp, res;
286 addr = addr >> 2; /* temporary hack. */
287 if (addr < 0 || addr >= 17)
288 return -EIO;
289 res = verify_area(VERIFY_WRITE, (void *) data, 4);
290 if (res)
291 return res;
292 tmp = get_stack_long(child, 4*addr - MAGICNUMBER);
293 put_fs_long(tmp,(unsigned long *) data);
294 return 0;
295 }
296
297 /* when I and D space are seperate, this will have to be fixed. */
298 case PTRACE_POKETEXT: /* write the word at location addr. */
299 case PTRACE_POKEDATA:
300 return write_long(child,addr,data);
301
302 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
303 addr = addr >> 2; /* temproary hack. */
304 if (addr < 0 || addr >= 17)
305 return -EIO;
306 if (addr == ORIG_EAX)
307 return -EIO;
308 if (addr == EFL) { /* flags. */
309 data &= FLAG_MASK;
310 data |= get_stack_long(child, EFL*4-MAGICNUMBER) & ~FLAG_MASK;
311 }
312 if (put_stack_long(child, 4*addr-MAGICNUMBER, data))
313 return -EIO;
314 return 0;
315
316 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
317 case PTRACE_CONT: { /* restart after signal. */
318 long tmp;
319
320 if (request == PTRACE_SYSCALL)
321 child->flags |= PF_TRACESYS;
322 else
323 child->flags &= ~PF_TRACESYS;
324 child->signal = 0;
325 if (data > 0 && data <= NSIG)
326 child->signal = 1<<(data-1);
327 child->state = TASK_RUNNING;
328 /* make sure the single step bit is not set. */
329 tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG;
330 put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
331 return 0;
332 }
333
334 /*
335 * make the child exit. Best I can do is send it a sigkill.
336 * perhaps it should be put in the status that it want's to
337 * exit.
338 */
339 case PTRACE_KILL: {
340 long tmp;
341
342 child->state = TASK_RUNNING;
343 child->signal = 1 << (SIGKILL-1);
344 /* make sure the single step bit is not set. */
345 tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG;
346 put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
347 return 0;
348 }
349
350 case PTRACE_SINGLESTEP: { /* set the trap flag. */
351 long tmp;
352
353 child->flags &= ~PF_TRACESYS;
354 tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) | TRAP_FLAG;
355 put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
356 child->state = TASK_RUNNING;
357 child->signal = 0;
358 if (data > 0 && data <= NSIG)
359 child->signal= 1<<(data-1);
360 /* give it a chance to run. */
361 return 0;
362 }
363
364 case PTRACE_DETACH: { /* detach a process that was attached. */
365 long tmp;
366
367 child->flags &= ~(PF_PTRACED|PF_TRACESYS);
368 child->signal=0;
369 child->state = 0;
370 REMOVE_LINKS(child);
371 child->p_pptr = child->p_opptr;
372 SET_LINKS(child);
373 /* make sure the single step bit is not set. */
374 tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG;
375 put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
376 return 0;
377 }
378
379 default:
380 return -EIO;
381 }
382 }