1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33#include <linux/kernel.h>
34#include <linux/sched.h>
35#include <linux/mm.h>
36#include <linux/smp.h>
37#include <linux/smp_lock.h>
38#include <linux/errno.h>
39#include <linux/ptrace.h>
40#include <linux/user.h>
41
42#include <asm/uaccess.h>
43#include <asm/page.h>
44#include <asm/pgtable.h>
45#include <asm/system.h>
46#include <asm/processor.h>
47
48
49
50
51
52
53
54
55#define DCCR_MASK 0x0000001f
56
57
58
59
60static inline long get_reg(struct task_struct *task, unsigned int regno)
61{
62
63
64
65
66 if (regno == PT_USP)
67 return task->thread.usp;
68 else if (regno < PT_MAX)
69 return ((unsigned long *)user_regs(task))[regno];
70 else
71 return 0;
72}
73
74
75
76
77static inline int put_reg(struct task_struct *task, unsigned int regno,
78 unsigned long data)
79{
80 if (regno == PT_USP)
81 task->thread.usp = data;
82 else if (regno < PT_MAX)
83 ((unsigned long *)user_regs(task))[regno] = data;
84 else
85 return -1;
86 return 0;
87}
88
89
90
91
92
93
94void ptrace_disable(struct task_struct *child)
95{
96
97}
98
99
100
101
102
103
104
105
106
107asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
108{
109 struct task_struct *child;
110 int ret;
111
112 lock_kernel();
113 ret = -EPERM;
114 if (request == PTRACE_TRACEME) {
115
116 if (current->ptrace & PT_PTRACED)
117 goto out;
118
119 current->ptrace |= PT_PTRACED;
120 ret = 0;
121 goto out;
122 }
123 ret = -ESRCH;
124 read_lock(&tasklist_lock);
125 child = find_task_by_pid(pid);
126 if (child)
127 get_task_struct(child);
128 read_unlock(&tasklist_lock);
129 if (!child)
130 goto out;
131 ret = -EPERM;
132 if (pid == 1)
133 goto out_tsk;
134 if (request == PTRACE_ATTACH) {
135 ret = ptrace_attach(child);
136 goto out_tsk;
137 }
138 ret = -ESRCH;
139 if (!(child->ptrace & PT_PTRACED))
140 goto out_tsk;
141 if (child->state != TASK_STOPPED) {
142 if (request != PTRACE_KILL)
143 goto out_tsk;
144 }
145 if (child->p_pptr != current)
146 goto out_tsk;
147
148 switch (request) {
149
150 case PTRACE_PEEKTEXT:
151 case PTRACE_PEEKDATA: {
152 unsigned long tmp;
153 int copied;
154
155 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
156 ret = -EIO;
157 if (copied != sizeof(tmp))
158 break;
159 ret = put_user(tmp,(unsigned long *) data);
160 break;
161 }
162
163
164 case PTRACE_PEEKUSR: {
165 unsigned long tmp;
166
167 ret = -EIO;
168 if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
169 break;
170
171 tmp = 0;
172 ret = -EIO;
173 if (addr < sizeof(struct pt_regs)) {
174 tmp = get_reg(child, addr >> 2);
175 ret = put_user(tmp, (unsigned long *)data);
176 }
177 break;
178 }
179
180
181 case PTRACE_POKETEXT:
182 case PTRACE_POKEDATA:
183 ret = 0;
184 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
185 break;
186 ret = -EIO;
187 break;
188
189 case PTRACE_POKEUSR:
190 ret = -EIO;
191 if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
192 break;
193
194 if (addr < sizeof(struct pt_regs)) {
195 addr >>= 2;
196
197 if (addr == PT_DCCR) {
198
199
200
201 data &= DCCR_MASK;
202 data |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
203 }
204 if (put_reg(child, addr, data))
205 break;
206 ret = 0;
207 }
208 break;
209
210 case PTRACE_SYSCALL:
211 case PTRACE_CONT:
212 ret = -EIO;
213 if ((unsigned long) data > _NSIG)
214 break;
215 if (request == PTRACE_SYSCALL)
216 child->ptrace |= PT_TRACESYS;
217 else
218 child->ptrace &= ~PT_TRACESYS;
219 child->exit_code = data;
220
221 wake_up_process(child);
222 ret = 0;
223 break;
224
225
226
227
228
229
230 case PTRACE_KILL:
231 ret = 0;
232 if (child->state == TASK_ZOMBIE)
233 break;
234 child->exit_code = SIGKILL;
235
236 wake_up_process(child);
237 break;
238
239 case PTRACE_SINGLESTEP:
240 ret = -EIO;
241 if ((unsigned long) data > _NSIG)
242 break;
243 child->ptrace &= ~PT_TRACESYS;
244
245
246
247 child->exit_code = data;
248
249 wake_up_process(child);
250 ret = 0;
251 break;
252
253 case PTRACE_DETACH:
254 ret = ptrace_detach(child, data);
255 break;
256
257 case PTRACE_GETREGS: {
258 int i;
259 unsigned long tmp;
260 for (i = 0; i <= PT_MAX; i++) {
261 tmp = get_reg(child, i);
262 if (put_user(tmp, (unsigned long *) data)) {
263 ret = -EFAULT;
264 break;
265 }
266 data += sizeof(long);
267 }
268 ret = 0;
269 break;
270 }
271
272 case PTRACE_SETREGS: {
273 int i;
274 unsigned long tmp;
275 for (i = 0; i <= PT_MAX; i++) {
276 if (get_user(tmp, (unsigned long *) data)) {
277 ret = -EFAULT;
278 break;
279 }
280 if (i == PT_DCCR) {
281 tmp &= DCCR_MASK;
282 tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
283 }
284 put_reg(child, i, tmp);
285 data += sizeof(long);
286 }
287 ret = 0;
288 break;
289 }
290
291 default:
292 ret = -EIO;
293 break;
294 }
295out_tsk:
296 free_task_struct(child);
297out:
298 unlock_kernel();
299 return ret;
300}
301
302asmlinkage void syscall_trace(void)
303{
304 if ((current->ptrace & (PT_PTRACED | PT_TRACESYS)) !=
305 (PT_PTRACED | PT_TRACESYS))
306 return;
307
308
309
310 current->exit_code = SIGTRAP;
311 current->state = TASK_STOPPED;
312 notify_parent(current, SIGCHLD);
313 schedule();
314
315
316
317
318
319 if (current->exit_code) {
320 send_sig(current->exit_code, current, 1);
321 current->exit_code = 0;
322 }
323}
324