1
2
3
4
5
6
7
8
9
10
11
12#include <linux/kernel.h>
13#include <linux/sched.h>
14#include <linux/mm.h>
15#include <linux/errno.h>
16#include <linux/ptrace.h>
17#include <linux/user.h>
18#include <linux/smp.h>
19#include <linux/smp_lock.h>
20#include <linux/security.h>
21#include <linux/signal.h>
22
23#include <asm/pgtable.h>
24#include <asm/system.h>
25#include <asm/uaccess.h>
26
27#define MAGIC_CONSTANT 0x80000000
28
29
30
31
32
33
34
35static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
36{
37 regs->u_regs[UREG_I0] = error;
38 regs->psr |= PSR_C;
39 regs->pc = regs->npc;
40 regs->npc += 4;
41}
42
43static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
44{
45 regs->u_regs[UREG_I0] = value;
46 regs->psr &= ~PSR_C;
47 regs->pc = regs->npc;
48 regs->npc += 4;
49}
50
51static void
52pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long __user *addr)
53{
54 if (put_user(value, addr)) {
55 pt_error_return(regs, EFAULT);
56 return;
57 }
58 regs->u_regs[UREG_I0] = 0;
59 regs->psr &= ~PSR_C;
60 regs->pc = regs->npc;
61 regs->npc += 4;
62}
63
64static void
65pt_os_succ_return (struct pt_regs *regs, unsigned long val, long __user *addr)
66{
67 if (current->personality == PER_SUNOS)
68 pt_succ_return (regs, val);
69 else
70 pt_succ_return_linux (regs, val, addr);
71}
72
73
74static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset,
75 struct task_struct *tsk, long __user *addr)
76{
77 struct pt_regs *cregs = tsk->thread.kregs;
78 struct thread_info *t = task_thread_info(tsk);
79 int v;
80
81 if(offset >= 1024)
82 offset -= 1024;
83 if(offset & ((sizeof(unsigned long) - 1))) {
84 pt_error_return(regs, EIO);
85 return;
86 }
87 if(offset >= 16 && offset < 784) {
88 offset -= 16; offset >>= 2;
89 pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr);
90 return;
91 }
92 if(offset >= 784 && offset < 832) {
93 offset -= 784; offset >>= 2;
94 pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr);
95 return;
96 }
97 switch(offset) {
98 case 0:
99 v = t->ksp;
100 break;
101 case 4:
102 v = t->kpc;
103 break;
104 case 8:
105 v = t->kpsr;
106 break;
107 case 12:
108 v = t->uwinmask;
109 break;
110 case 832:
111 v = t->w_saved;
112 break;
113 case 896:
114 v = cregs->u_regs[UREG_I0];
115 break;
116 case 900:
117 v = cregs->u_regs[UREG_I1];
118 break;
119 case 904:
120 v = cregs->u_regs[UREG_I2];
121 break;
122 case 908:
123 v = cregs->u_regs[UREG_I3];
124 break;
125 case 912:
126 v = cregs->u_regs[UREG_I4];
127 break;
128 case 916:
129 v = cregs->u_regs[UREG_I5];
130 break;
131 case 920:
132 v = cregs->u_regs[UREG_I6];
133 break;
134 case 924:
135 if(tsk->thread.flags & MAGIC_CONSTANT)
136 v = cregs->u_regs[UREG_G1];
137 else
138 v = 0;
139 break;
140 case 940:
141 v = cregs->u_regs[UREG_I0];
142 break;
143 case 944:
144 v = cregs->u_regs[UREG_I1];
145 break;
146
147 case 948:
148
149 if(cregs->psr & PSR_C)
150 v = cregs->u_regs[UREG_I0] << 24;
151 else
152 v = 0;
153 break;
154
155
156 default:
157 printk("%s [%d]: Wants to read user offset %ld\n",
158 current->comm, task_pid_nr(current), offset);
159 pt_error_return(regs, EIO);
160 return;
161 }
162 if (current->personality == PER_SUNOS)
163 pt_succ_return (regs, v);
164 else
165 pt_succ_return_linux (regs, v, addr);
166 return;
167}
168
169static inline void write_sunos_user(struct pt_regs *regs, unsigned long offset,
170 struct task_struct *tsk)
171{
172 struct pt_regs *cregs = tsk->thread.kregs;
173 struct thread_info *t = task_thread_info(tsk);
174 unsigned long value = regs->u_regs[UREG_I3];
175
176 if(offset >= 1024)
177 offset -= 1024;
178 if(offset & ((sizeof(unsigned long) - 1)))
179 goto failure;
180 if(offset >= 16 && offset < 784) {
181 offset -= 16; offset >>= 2;
182 *(((unsigned long *)(&t->reg_window[0]))+offset) = value;
183 goto success;
184 }
185 if(offset >= 784 && offset < 832) {
186 offset -= 784; offset >>= 2;
187 *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value;
188 goto success;
189 }
190 switch(offset) {
191 case 896:
192 cregs->u_regs[UREG_I0] = value;
193 break;
194 case 900:
195 cregs->u_regs[UREG_I1] = value;
196 break;
197 case 904:
198 cregs->u_regs[UREG_I2] = value;
199 break;
200 case 908:
201 cregs->u_regs[UREG_I3] = value;
202 break;
203 case 912:
204 cregs->u_regs[UREG_I4] = value;
205 break;
206 case 916:
207 cregs->u_regs[UREG_I5] = value;
208 break;
209 case 920:
210 cregs->u_regs[UREG_I6] = value;
211 break;
212 case 924:
213 cregs->u_regs[UREG_I7] = value;
214 break;
215 case 940:
216 cregs->u_regs[UREG_I0] = value;
217 break;
218 case 944:
219 cregs->u_regs[UREG_I1] = value;
220 break;
221
222
223 default:
224 printk("%s [%d]: Wants to write user offset %ld\n",
225 current->comm, task_pid_nr(current), offset);
226 goto failure;
227 }
228success:
229 pt_succ_return(regs, 0);
230 return;
231failure:
232 pt_error_return(regs, EIO);
233 return;
234}
235
236
237
238
239#ifdef DEBUG_PTRACE
240char *pt_rq [] = {
241 "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR",
242 "POKETEXT", "POKEDATA", "POKEUSR", "CONT",
243 "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH",
244 "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS",
245 "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT",
246 "GETFPAREGS", "SETFPAREGS", "unknown", "unknown",
247 "SYSCALL", ""
248};
249#endif
250
251
252
253
254
255
256void ptrace_disable(struct task_struct *child)
257{
258
259}
260
261asmlinkage void do_ptrace(struct pt_regs *regs)
262{
263 unsigned long request = regs->u_regs[UREG_I0];
264 unsigned long pid = regs->u_regs[UREG_I1];
265 unsigned long addr = regs->u_regs[UREG_I2];
266 unsigned long data = regs->u_regs[UREG_I3];
267 unsigned long addr2 = regs->u_regs[UREG_I4];
268 struct task_struct *child;
269 int ret;
270
271 lock_kernel();
272#ifdef DEBUG_PTRACE
273 {
274 char *s;
275
276 if ((request >= 0) && (request <= 24))
277 s = pt_rq [request];
278 else
279 s = "unknown";
280
281 if (request == PTRACE_POKEDATA && data == 0x91d02001){
282 printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n",
283 pid, addr, addr2);
284 } else
285 printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n",
286 s, (int) request, (int) pid, addr, data, addr2);
287 }
288#endif
289
290 if (request == PTRACE_TRACEME) {
291 ret = ptrace_traceme();
292 if (ret < 0)
293 pt_error_return(regs, -ret);
294 else
295 pt_succ_return(regs, 0);
296 goto out;
297 }
298
299 child = ptrace_get_task_struct(pid);
300 if (IS_ERR(child)) {
301 ret = PTR_ERR(child);
302 pt_error_return(regs, -ret);
303 goto out;
304 }
305
306 if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
307 || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
308 if (ptrace_attach(child)) {
309 pt_error_return(regs, EPERM);
310 goto out_tsk;
311 }
312 pt_succ_return(regs, 0);
313 goto out_tsk;
314 }
315
316 ret = ptrace_check_attach(child, request == PTRACE_KILL);
317 if (ret < 0) {
318 pt_error_return(regs, -ret);
319 goto out_tsk;
320 }
321
322 switch(request) {
323 case PTRACE_PEEKTEXT:
324 case PTRACE_PEEKDATA: {
325 unsigned long tmp;
326
327 if (access_process_vm(child, addr,
328 &tmp, sizeof(tmp), 0) == sizeof(tmp))
329 pt_os_succ_return(regs, tmp, (long __user *)data);
330 else
331 pt_error_return(regs, EIO);
332 goto out_tsk;
333 }
334
335 case PTRACE_PEEKUSR:
336 read_sunos_user(regs, addr, child, (long __user *) data);
337 goto out_tsk;
338
339 case PTRACE_POKEUSR:
340 write_sunos_user(regs, addr, child);
341 goto out_tsk;
342
343 case PTRACE_POKETEXT:
344 case PTRACE_POKEDATA: {
345 if (access_process_vm(child, addr,
346 &data, sizeof(data), 1) == sizeof(data))
347 pt_succ_return(regs, 0);
348 else
349 pt_error_return(regs, EIO);
350 goto out_tsk;
351 }
352
353 case PTRACE_GETREGS: {
354 struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
355 struct pt_regs *cregs = child->thread.kregs;
356 int rval;
357
358 if (!access_ok(VERIFY_WRITE, pregs, sizeof(struct pt_regs))) {
359 rval = -EFAULT;
360 pt_error_return(regs, -rval);
361 goto out_tsk;
362 }
363 __put_user(cregs->psr, (&pregs->psr));
364 __put_user(cregs->pc, (&pregs->pc));
365 __put_user(cregs->npc, (&pregs->npc));
366 __put_user(cregs->y, (&pregs->y));
367 for(rval = 1; rval < 16; rval++)
368 __put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]));
369 pt_succ_return(regs, 0);
370#ifdef DEBUG_PTRACE
371 printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]);
372#endif
373 goto out_tsk;
374 }
375
376 case PTRACE_SETREGS: {
377 struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
378 struct pt_regs *cregs = child->thread.kregs;
379 unsigned long psr, pc, npc, y;
380 int i;
381
382
383
384
385 if (!access_ok(VERIFY_READ, pregs, sizeof(struct pt_regs))) {
386 pt_error_return(regs, EFAULT);
387 goto out_tsk;
388 }
389 __get_user(psr, (&pregs->psr));
390 __get_user(pc, (&pregs->pc));
391 __get_user(npc, (&pregs->npc));
392 __get_user(y, (&pregs->y));
393 psr &= PSR_ICC;
394 cregs->psr &= ~PSR_ICC;
395 cregs->psr |= psr;
396 if (!((pc | npc) & 3)) {
397 cregs->pc = pc;
398 cregs->npc =npc;
399 }
400 cregs->y = y;
401 for(i = 1; i < 16; i++)
402 __get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]));
403 pt_succ_return(regs, 0);
404 goto out_tsk;
405 }
406
407 case PTRACE_GETFPREGS: {
408 struct fps {
409 unsigned long regs[32];
410 unsigned long fsr;
411 unsigned long flags;
412 unsigned long extra;
413 unsigned long fpqd;
414 struct fq {
415 unsigned long *insnaddr;
416 unsigned long insn;
417 } fpq[16];
418 };
419 struct fps __user *fps = (struct fps __user *) addr;
420 int i;
421
422 if (!access_ok(VERIFY_WRITE, fps, sizeof(struct fps))) {
423 i = -EFAULT;
424 pt_error_return(regs, -i);
425 goto out_tsk;
426 }
427 for(i = 0; i < 32; i++)
428 __put_user(child->thread.float_regs[i], (&fps->regs[i]));
429 __put_user(child->thread.fsr, (&fps->fsr));
430 __put_user(child->thread.fpqdepth, (&fps->fpqd));
431 __put_user(0, (&fps->flags));
432 __put_user(0, (&fps->extra));
433 for(i = 0; i < 16; i++) {
434 __put_user(child->thread.fpqueue[i].insn_addr,
435 (&fps->fpq[i].insnaddr));
436 __put_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
437 }
438 pt_succ_return(regs, 0);
439 goto out_tsk;
440 }
441
442 case PTRACE_SETFPREGS: {
443 struct fps {
444 unsigned long regs[32];
445 unsigned long fsr;
446 unsigned long flags;
447 unsigned long extra;
448 unsigned long fpqd;
449 struct fq {
450 unsigned long *insnaddr;
451 unsigned long insn;
452 } fpq[16];
453 };
454 struct fps __user *fps = (struct fps __user *) addr;
455 int i;
456
457 if (!access_ok(VERIFY_READ, fps, sizeof(struct fps))) {
458 i = -EFAULT;
459 pt_error_return(regs, -i);
460 goto out_tsk;
461 }
462 copy_from_user(&child->thread.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long)));
463 __get_user(child->thread.fsr, (&fps->fsr));
464 __get_user(child->thread.fpqdepth, (&fps->fpqd));
465 for(i = 0; i < 16; i++) {
466 __get_user(child->thread.fpqueue[i].insn_addr,
467 (&fps->fpq[i].insnaddr));
468 __get_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn));
469 }
470 pt_succ_return(regs, 0);
471 goto out_tsk;
472 }
473
474 case PTRACE_READTEXT:
475 case PTRACE_READDATA: {
476 int res = ptrace_readdata(child, addr,
477 (void __user *) addr2, data);
478
479 if (res == data) {
480 pt_succ_return(regs, 0);
481 goto out_tsk;
482 }
483
484 if (res >= 0)
485 res = -EIO;
486 pt_error_return(regs, -res);
487 goto out_tsk;
488 }
489
490 case PTRACE_WRITETEXT:
491 case PTRACE_WRITEDATA: {
492 int res = ptrace_writedata(child, (void __user *) addr2,
493 addr, data);
494
495 if (res == data) {
496 pt_succ_return(regs, 0);
497 goto out_tsk;
498 }
499
500 if (res >= 0)
501 res = -EIO;
502 pt_error_return(regs, -res);
503 goto out_tsk;
504 }
505
506 case PTRACE_SYSCALL:
507 addr = 1;
508
509 case PTRACE_CONT: {
510 if (!valid_signal(data)) {
511 pt_error_return(regs, EIO);
512 goto out_tsk;
513 }
514
515 if (request == PTRACE_SYSCALL)
516 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
517 else
518 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
519
520 child->exit_code = data;
521#ifdef DEBUG_PTRACE
522 printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n",
523 child->comm, child->pid, child->exit_code,
524 child->thread.kregs->pc,
525 child->thread.kregs->npc);
526#endif
527 wake_up_process(child);
528 pt_succ_return(regs, 0);
529 goto out_tsk;
530 }
531
532
533
534
535
536
537 case PTRACE_KILL: {
538 if (child->exit_state == EXIT_ZOMBIE) {
539 pt_succ_return(regs, 0);
540 goto out_tsk;
541 }
542 wake_up_process(child);
543 child->exit_code = SIGKILL;
544 pt_succ_return(regs, 0);
545 goto out_tsk;
546 }
547
548 case PTRACE_SUNDETACH: {
549 int err = ptrace_detach(child, data);
550 if (err) {
551 pt_error_return(regs, EIO);
552 goto out_tsk;
553 }
554 pt_succ_return(regs, 0);
555 goto out_tsk;
556 }
557
558
559
560 default: {
561 int err = ptrace_request(child, request, addr, data);
562 if (err)
563 pt_error_return(regs, -err);
564 else
565 pt_succ_return(regs, 0);
566 goto out_tsk;
567 }
568 }
569out_tsk:
570 if (child)
571 put_task_struct(child);
572out:
573 unlock_kernel();
574}
575
576asmlinkage void syscall_trace(void)
577{
578#ifdef DEBUG_PTRACE
579 printk("%s [%d]: syscall_trace\n", current->comm, current->pid);
580#endif
581 if (!test_thread_flag(TIF_SYSCALL_TRACE))
582 return;
583 if (!(current->ptrace & PT_PTRACED))
584 return;
585 current->thread.flags ^= MAGIC_CONSTANT;
586 ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
587 ? 0x80 : 0));
588
589
590
591
592
593#ifdef DEBUG_PTRACE
594 printk("%s [%d]: syscall_trace exit= %x\n", current->comm,
595 current->pid, current->exit_code);
596#endif
597 if (current->exit_code) {
598 send_sig (current->exit_code, current, 1);
599 current->exit_code = 0;
600 }
601}
602