Contents of /alx-src/tags/kernel26-2.6.12-alx-r9/kernel/ptrace.c
Parent Directory | Revision Log
Revision 630 -
(show annotations)
(download)
Wed Mar 4 11:03:09 2009 UTC (15 years, 6 months ago) by niro
File MIME type: text/plain
File size: 8960 byte(s)
Wed Mar 4 11:03:09 2009 UTC (15 years, 6 months ago) by niro
File MIME type: text/plain
File size: 8960 byte(s)
Tag kernel26-2.6.12-alx-r9
1 | /* |
2 | * linux/kernel/ptrace.c |
3 | * |
4 | * (C) Copyright 1999 Linus Torvalds |
5 | * |
6 | * Common interfaces for "ptrace()" which we do not want |
7 | * to continually duplicate across every architecture. |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/sched.h> |
12 | #include <linux/errno.h> |
13 | #include <linux/mm.h> |
14 | #include <linux/highmem.h> |
15 | #include <linux/pagemap.h> |
16 | #include <linux/smp_lock.h> |
17 | #include <linux/ptrace.h> |
18 | #include <linux/security.h> |
19 | #include <linux/signal.h> |
20 | |
21 | #include <asm/pgtable.h> |
22 | #include <asm/uaccess.h> |
23 | |
24 | /* |
25 | * ptrace a task: make the debugger its new parent and |
26 | * move it to the ptrace list. |
27 | * |
28 | * Must be called with the tasklist lock write-held. |
29 | */ |
30 | void __ptrace_link(task_t *child, task_t *new_parent) |
31 | { |
32 | if (!list_empty(&child->ptrace_list)) |
33 | BUG(); |
34 | if (child->parent == new_parent) |
35 | return; |
36 | list_add(&child->ptrace_list, &child->parent->ptrace_children); |
37 | REMOVE_LINKS(child); |
38 | child->parent = new_parent; |
39 | SET_LINKS(child); |
40 | } |
41 | |
42 | /* |
43 | * Turn a tracing stop into a normal stop now, since with no tracer there |
44 | * would be no way to wake it up with SIGCONT or SIGKILL. If there was a |
45 | * signal sent that would resume the child, but didn't because it was in |
46 | * TASK_TRACED, resume it now. |
47 | * Requires that irqs be disabled. |
48 | */ |
49 | void ptrace_untrace(task_t *child) |
50 | { |
51 | spin_lock(&child->sighand->siglock); |
52 | if (child->state == TASK_TRACED) { |
53 | if (child->signal->flags & SIGNAL_STOP_STOPPED) { |
54 | child->state = TASK_STOPPED; |
55 | } else { |
56 | signal_wake_up(child, 1); |
57 | } |
58 | } |
59 | spin_unlock(&child->sighand->siglock); |
60 | } |
61 | |
62 | /* |
63 | * unptrace a task: move it back to its original parent and |
64 | * remove it from the ptrace list. |
65 | * |
66 | * Must be called with the tasklist lock write-held. |
67 | */ |
68 | void __ptrace_unlink(task_t *child) |
69 | { |
70 | if (!child->ptrace) |
71 | BUG(); |
72 | child->ptrace = 0; |
73 | if (!list_empty(&child->ptrace_list)) { |
74 | list_del_init(&child->ptrace_list); |
75 | REMOVE_LINKS(child); |
76 | child->parent = child->real_parent; |
77 | SET_LINKS(child); |
78 | } |
79 | |
80 | if (child->state == TASK_TRACED) |
81 | ptrace_untrace(child); |
82 | } |
83 | |
84 | /* |
85 | * Check that we have indeed attached to the thing.. |
86 | */ |
87 | int ptrace_check_attach(struct task_struct *child, int kill) |
88 | { |
89 | int ret = -ESRCH; |
90 | |
91 | /* |
92 | * We take the read lock around doing both checks to close a |
93 | * possible race where someone else was tracing our child and |
94 | * detached between these two checks. After this locked check, |
95 | * we are sure that this is our traced child and that can only |
96 | * be changed by us so it's not changing right after this. |
97 | */ |
98 | read_lock(&tasklist_lock); |
99 | if ((child->ptrace & PT_PTRACED) && child->parent == current && |
100 | (!(child->ptrace & PT_ATTACHED) || child->real_parent != current) |
101 | && child->signal != NULL) { |
102 | ret = 0; |
103 | spin_lock_irq(&child->sighand->siglock); |
104 | if (child->state == TASK_STOPPED) { |
105 | child->state = TASK_TRACED; |
106 | } else if (child->state != TASK_TRACED && !kill) { |
107 | ret = -ESRCH; |
108 | } |
109 | spin_unlock_irq(&child->sighand->siglock); |
110 | } |
111 | read_unlock(&tasklist_lock); |
112 | |
113 | if (!ret && !kill) { |
114 | wait_task_inactive(child); |
115 | } |
116 | |
117 | /* All systems go.. */ |
118 | return ret; |
119 | } |
120 | |
121 | int ptrace_attach(struct task_struct *task) |
122 | { |
123 | int retval; |
124 | task_lock(task); |
125 | retval = -EPERM; |
126 | if (task->pid <= 1) |
127 | goto bad; |
128 | if (task == current) |
129 | goto bad; |
130 | if (!task->mm) |
131 | goto bad; |
132 | if(((current->uid != task->euid) || |
133 | (current->uid != task->suid) || |
134 | (current->uid != task->uid) || |
135 | (current->gid != task->egid) || |
136 | (current->gid != task->sgid) || |
137 | (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) |
138 | goto bad; |
139 | smp_rmb(); |
140 | if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE)) |
141 | goto bad; |
142 | /* the same process cannot be attached many times */ |
143 | if (task->ptrace & PT_PTRACED) |
144 | goto bad; |
145 | retval = security_ptrace(current, task); |
146 | if (retval) |
147 | goto bad; |
148 | |
149 | /* Go */ |
150 | task->ptrace |= PT_PTRACED | ((task->real_parent != current) |
151 | ? PT_ATTACHED : 0); |
152 | if (capable(CAP_SYS_PTRACE)) |
153 | task->ptrace |= PT_PTRACE_CAP; |
154 | task_unlock(task); |
155 | |
156 | write_lock_irq(&tasklist_lock); |
157 | __ptrace_link(task, current); |
158 | write_unlock_irq(&tasklist_lock); |
159 | |
160 | force_sig_specific(SIGSTOP, task); |
161 | return 0; |
162 | |
163 | bad: |
164 | task_unlock(task); |
165 | return retval; |
166 | } |
167 | |
168 | int ptrace_detach(struct task_struct *child, unsigned int data) |
169 | { |
170 | if (!valid_signal(data)) |
171 | return -EIO; |
172 | |
173 | /* Architecture-specific hardware disable .. */ |
174 | ptrace_disable(child); |
175 | |
176 | /* .. re-parent .. */ |
177 | child->exit_code = data; |
178 | |
179 | write_lock_irq(&tasklist_lock); |
180 | __ptrace_unlink(child); |
181 | /* .. and wake it up. */ |
182 | if (child->exit_state != EXIT_ZOMBIE) |
183 | wake_up_process(child); |
184 | write_unlock_irq(&tasklist_lock); |
185 | |
186 | return 0; |
187 | } |
188 | |
189 | /* |
190 | * Access another process' address space. |
191 | * Source/target buffer must be kernel space, |
192 | * Do not walk the page table directly, use get_user_pages |
193 | */ |
194 | |
195 | int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write) |
196 | { |
197 | struct mm_struct *mm; |
198 | struct vm_area_struct *vma; |
199 | struct page *page; |
200 | void *old_buf = buf; |
201 | |
202 | mm = get_task_mm(tsk); |
203 | if (!mm) |
204 | return 0; |
205 | |
206 | down_read(&mm->mmap_sem); |
207 | /* ignore errors, just check how much was sucessfully transfered */ |
208 | while (len) { |
209 | int bytes, ret, offset; |
210 | void *maddr; |
211 | |
212 | ret = get_user_pages(tsk, mm, addr, 1, |
213 | write, 1, &page, &vma); |
214 | if (ret <= 0) |
215 | break; |
216 | |
217 | bytes = len; |
218 | offset = addr & (PAGE_SIZE-1); |
219 | if (bytes > PAGE_SIZE-offset) |
220 | bytes = PAGE_SIZE-offset; |
221 | |
222 | maddr = kmap(page); |
223 | if (write) { |
224 | copy_to_user_page(vma, page, addr, |
225 | maddr + offset, buf, bytes); |
226 | set_page_dirty_lock(page); |
227 | } else { |
228 | copy_from_user_page(vma, page, addr, |
229 | buf, maddr + offset, bytes); |
230 | } |
231 | kunmap(page); |
232 | page_cache_release(page); |
233 | len -= bytes; |
234 | buf += bytes; |
235 | addr += bytes; |
236 | } |
237 | up_read(&mm->mmap_sem); |
238 | mmput(mm); |
239 | |
240 | return buf - old_buf; |
241 | } |
242 | |
243 | int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) |
244 | { |
245 | int copied = 0; |
246 | |
247 | while (len > 0) { |
248 | char buf[128]; |
249 | int this_len, retval; |
250 | |
251 | this_len = (len > sizeof(buf)) ? sizeof(buf) : len; |
252 | retval = access_process_vm(tsk, src, buf, this_len, 0); |
253 | if (!retval) { |
254 | if (copied) |
255 | break; |
256 | return -EIO; |
257 | } |
258 | if (copy_to_user(dst, buf, retval)) |
259 | return -EFAULT; |
260 | copied += retval; |
261 | src += retval; |
262 | dst += retval; |
263 | len -= retval; |
264 | } |
265 | return copied; |
266 | } |
267 | |
268 | int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len) |
269 | { |
270 | int copied = 0; |
271 | |
272 | while (len > 0) { |
273 | char buf[128]; |
274 | int this_len, retval; |
275 | |
276 | this_len = (len > sizeof(buf)) ? sizeof(buf) : len; |
277 | if (copy_from_user(buf, src, this_len)) |
278 | return -EFAULT; |
279 | retval = access_process_vm(tsk, dst, buf, this_len, 1); |
280 | if (!retval) { |
281 | if (copied) |
282 | break; |
283 | return -EIO; |
284 | } |
285 | copied += retval; |
286 | src += retval; |
287 | dst += retval; |
288 | len -= retval; |
289 | } |
290 | return copied; |
291 | } |
292 | |
293 | static int ptrace_setoptions(struct task_struct *child, long data) |
294 | { |
295 | child->ptrace &= ~PT_TRACE_MASK; |
296 | |
297 | if (data & PTRACE_O_TRACESYSGOOD) |
298 | child->ptrace |= PT_TRACESYSGOOD; |
299 | |
300 | if (data & PTRACE_O_TRACEFORK) |
301 | child->ptrace |= PT_TRACE_FORK; |
302 | |
303 | if (data & PTRACE_O_TRACEVFORK) |
304 | child->ptrace |= PT_TRACE_VFORK; |
305 | |
306 | if (data & PTRACE_O_TRACECLONE) |
307 | child->ptrace |= PT_TRACE_CLONE; |
308 | |
309 | if (data & PTRACE_O_TRACEEXEC) |
310 | child->ptrace |= PT_TRACE_EXEC; |
311 | |
312 | if (data & PTRACE_O_TRACEVFORKDONE) |
313 | child->ptrace |= PT_TRACE_VFORK_DONE; |
314 | |
315 | if (data & PTRACE_O_TRACEEXIT) |
316 | child->ptrace |= PT_TRACE_EXIT; |
317 | |
318 | return (data & ~PTRACE_O_MASK) ? -EINVAL : 0; |
319 | } |
320 | |
321 | static int ptrace_getsiginfo(struct task_struct *child, siginfo_t __user * data) |
322 | { |
323 | siginfo_t lastinfo; |
324 | int error = -ESRCH; |
325 | |
326 | read_lock(&tasklist_lock); |
327 | if (likely(child->sighand != NULL)) { |
328 | error = -EINVAL; |
329 | spin_lock_irq(&child->sighand->siglock); |
330 | if (likely(child->last_siginfo != NULL)) { |
331 | lastinfo = *child->last_siginfo; |
332 | error = 0; |
333 | } |
334 | spin_unlock_irq(&child->sighand->siglock); |
335 | } |
336 | read_unlock(&tasklist_lock); |
337 | if (!error) |
338 | return copy_siginfo_to_user(data, &lastinfo); |
339 | return error; |
340 | } |
341 | |
342 | static int ptrace_setsiginfo(struct task_struct *child, siginfo_t __user * data) |
343 | { |
344 | siginfo_t newinfo; |
345 | int error = -ESRCH; |
346 | |
347 | if (copy_from_user(&newinfo, data, sizeof (siginfo_t))) |
348 | return -EFAULT; |
349 | |
350 | read_lock(&tasklist_lock); |
351 | if (likely(child->sighand != NULL)) { |
352 | error = -EINVAL; |
353 | spin_lock_irq(&child->sighand->siglock); |
354 | if (likely(child->last_siginfo != NULL)) { |
355 | *child->last_siginfo = newinfo; |
356 | error = 0; |
357 | } |
358 | spin_unlock_irq(&child->sighand->siglock); |
359 | } |
360 | read_unlock(&tasklist_lock); |
361 | return error; |
362 | } |
363 | |
364 | int ptrace_request(struct task_struct *child, long request, |
365 | long addr, long data) |
366 | { |
367 | int ret = -EIO; |
368 | |
369 | switch (request) { |
370 | #ifdef PTRACE_OLDSETOPTIONS |
371 | case PTRACE_OLDSETOPTIONS: |
372 | #endif |
373 | case PTRACE_SETOPTIONS: |
374 | ret = ptrace_setoptions(child, data); |
375 | break; |
376 | case PTRACE_GETEVENTMSG: |
377 | ret = put_user(child->ptrace_message, (unsigned long __user *) data); |
378 | break; |
379 | case PTRACE_GETSIGINFO: |
380 | ret = ptrace_getsiginfo(child, (siginfo_t __user *) data); |
381 | break; |
382 | case PTRACE_SETSIGINFO: |
383 | ret = ptrace_setsiginfo(child, (siginfo_t __user *) data); |
384 | break; |
385 | default: |
386 | break; |
387 | } |
388 | |
389 | return ret; |
390 | } |