Commit | Line | Data |
---|---|---|
ba180fd4 JD |
1 | /* |
2 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) | |
1da177e4 LT |
3 | * Licensed under the GPL |
4 | */ | |
5 | ||
1bfa2317 AV |
6 | #include <linux/audit.h> |
7 | #include <linux/ptrace.h> | |
8 | #include <linux/sched.h> | |
9 | #include <linux/tracehook.h> | |
7c0f6ba6 | 10 | #include <linux/uaccess.h> |
da028d5e | 11 | #include <asm/ptrace-abi.h> |
1bd09508 CH |
12 | |
13 | void user_enable_single_step(struct task_struct *child) | |
14 | { | |
15 | child->ptrace |= PT_DTRACE; | |
16 | child->thread.singlestep_syscall = 0; | |
17 | ||
18 | #ifdef SUBARCH_SET_SINGLESTEPPING | |
19 | SUBARCH_SET_SINGLESTEPPING(child, 1); | |
20 | #endif | |
21 | } | |
22 | ||
23 | void user_disable_single_step(struct task_struct *child) | |
82c1c11b | 24 | { |
1bd09508 | 25 | child->ptrace &= ~PT_DTRACE; |
ba180fd4 | 26 | child->thread.singlestep_syscall = 0; |
82c1c11b BS |
27 | |
28 | #ifdef SUBARCH_SET_SINGLESTEPPING | |
1bd09508 | 29 | SUBARCH_SET_SINGLESTEPPING(child, 0); |
82c1c11b | 30 | #endif |
ba9950c8 | 31 | } |
82c1c11b | 32 | |
1da177e4 LT |
33 | /* |
34 | * Called by kernel/ptrace.c when detaching.. | |
35 | */ | |
36 | void ptrace_disable(struct task_struct *child) | |
ba180fd4 | 37 | { |
1bd09508 | 38 | user_disable_single_step(child); |
1da177e4 LT |
39 | } |
40 | ||
82c1c11b BS |
41 | extern int peek_user(struct task_struct * child, long addr, long data); |
42 | extern int poke_user(struct task_struct * child, long addr, long data); | |
43 | ||
9b05a69e NK |
44 | long arch_ptrace(struct task_struct *child, long request, |
45 | unsigned long addr, unsigned long data) | |
1da177e4 | 46 | { |
1da177e4 | 47 | int i, ret; |
9b05a69e | 48 | unsigned long __user *p = (void __user *)data; |
0a3d763f | 49 | void __user *vp = p; |
1da177e4 | 50 | |
1da177e4 | 51 | switch (request) { |
1da177e4 | 52 | /* read the word at location addr in the USER area. */ |
ba180fd4 JD |
53 | case PTRACE_PEEKUSR: |
54 | ret = peek_user(child, addr, data); | |
55 | break; | |
1da177e4 | 56 | |
ba180fd4 JD |
57 | /* write the word at location addr in the USER area */ |
58 | case PTRACE_POKEUSR: | |
59 | ret = poke_user(child, addr, data); | |
60 | break; | |
1da177e4 | 61 | |
86d6f2bf RD |
62 | case PTRACE_SYSEMU: |
63 | case PTRACE_SYSEMU_SINGLESTEP: | |
64 | ret = -EIO; | |
65 | break; | |
66 | ||
1da177e4 LT |
67 | #ifdef PTRACE_GETREGS |
68 | case PTRACE_GETREGS: { /* Get all gp regs from the child. */ | |
4d338e1a | 69 | if (!access_ok(VERIFY_WRITE, p, MAX_REG_OFFSET)) { |
1da177e4 LT |
70 | ret = -EIO; |
71 | break; | |
72 | } | |
73 | for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { | |
4d338e1a AV |
74 | __put_user(getreg(child, i), p); |
75 | p++; | |
1da177e4 LT |
76 | } |
77 | ret = 0; | |
78 | break; | |
79 | } | |
80 | #endif | |
81 | #ifdef PTRACE_SETREGS | |
82 | case PTRACE_SETREGS: { /* Set all gp regs in the child. */ | |
83 | unsigned long tmp = 0; | |
4d338e1a | 84 | if (!access_ok(VERIFY_READ, p, MAX_REG_OFFSET)) { |
1da177e4 LT |
85 | ret = -EIO; |
86 | break; | |
87 | } | |
88 | for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { | |
4d338e1a | 89 | __get_user(tmp, p); |
1da177e4 | 90 | putreg(child, i, tmp); |
4d338e1a | 91 | p++; |
1da177e4 LT |
92 | } |
93 | ret = 0; | |
94 | break; | |
95 | } | |
1da177e4 | 96 | #endif |
aa6758d4 | 97 | case PTRACE_GET_THREAD_AREA: |
0a3d763f | 98 | ret = ptrace_get_thread_area(child, addr, vp); |
aa6758d4 PBG |
99 | break; |
100 | ||
101 | case PTRACE_SET_THREAD_AREA: | |
8818b671 | 102 | ret = ptrace_set_thread_area(child, addr, vp); |
aa6758d4 PBG |
103 | break; |
104 | ||
1da177e4 LT |
105 | default: |
106 | ret = ptrace_request(child, request, addr, data); | |
e8012b58 JD |
107 | if (ret == -EIO) |
108 | ret = subarch_ptrace(child, request, addr, data); | |
1da177e4 LT |
109 | break; |
110 | } | |
481bed45 | 111 | |
1da177e4 LT |
112 | return ret; |
113 | } | |
114 | ||
99764fa4 | 115 | static void send_sigtrap(struct task_struct *tsk, struct uml_pt_regs *regs, |
1da177e4 LT |
116 | int error_code) |
117 | { | |
118 | struct siginfo info; | |
119 | ||
120 | memset(&info, 0, sizeof(info)); | |
121 | info.si_signo = SIGTRAP; | |
122 | info.si_code = TRAP_BRKPT; | |
123 | ||
124 | /* User-mode eip? */ | |
125 | info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL; | |
126 | ||
b60745b9 | 127 | /* Send us the fake SIGTRAP */ |
1da177e4 LT |
128 | force_sig_info(SIGTRAP, &info, tsk); |
129 | } | |
130 | ||
ba180fd4 JD |
131 | /* |
132 | * XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and | |
1da177e4 LT |
133 | * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check |
134 | */ | |
5334cdae | 135 | int syscall_trace_enter(struct pt_regs *regs) |
1da177e4 | 136 | { |
91397401 | 137 | audit_syscall_entry(UPT_SYSCALL_NR(®s->regs), |
1bfa2317 AV |
138 | UPT_SYSCALL_ARG1(®s->regs), |
139 | UPT_SYSCALL_ARG2(®s->regs), | |
140 | UPT_SYSCALL_ARG3(®s->regs), | |
141 | UPT_SYSCALL_ARG4(®s->regs)); | |
1da177e4 LT |
142 | |
143 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | |
5334cdae | 144 | return 0; |
1da177e4 | 145 | |
5334cdae | 146 | return tracehook_report_syscall_entry(regs); |
1bfa2317 | 147 | } |
1da177e4 | 148 | |
1bfa2317 AV |
149 | void syscall_trace_leave(struct pt_regs *regs) |
150 | { | |
151 | int ptraced = current->ptrace; | |
1da177e4 | 152 | |
1bfa2317 | 153 | audit_syscall_exit(regs); |
1da177e4 | 154 | |
1bfa2317 AV |
155 | /* Fake a debug trap */ |
156 | if (ptraced & PT_DTRACE) | |
157 | send_sigtrap(current, ®s->regs, 0); | |
158 | ||
159 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | |
160 | return; | |
161 | ||
162 | tracehook_report_syscall_exit(regs, 0); | |
163 | /* force do_signal() --> is_syscall() */ | |
164 | if (ptraced & PT_PTRACED) | |
165 | set_thread_flag(TIF_SIGPENDING); | |
1da177e4 | 166 | } |