1
2
3
4
5
6
7
8
9
10#include <linux/sched.h>
11#include <linux/errno.h>
12#include <linux/mm.h>
13#include <linux/highmem.h>
14#include <linux/pagemap.h>
15#include <linux/smp_lock.h>
16#include <linux/ptrace.h>
17
18#include <asm/pgtable.h>
19#include <asm/uaccess.h>
20
21
22
23
24
25
26
27void __ptrace_link(task_t *child, task_t *new_parent)
28{
29 if (!list_empty(&child->ptrace_list))
30 BUG();
31 if (child->parent == new_parent)
32 return;
33 list_add(&child->ptrace_list, &child->parent->ptrace_children);
34 REMOVE_LINKS(child);
35 child->parent = new_parent;
36 SET_LINKS(child);
37}
38
39
40
41
42
43
44
45void __ptrace_unlink(task_t *child)
46{
47 if (!child->ptrace)
48 BUG();
49 child->ptrace = 0;
50 if (list_empty(&child->ptrace_list))
51 return;
52 list_del_init(&child->ptrace_list);
53 REMOVE_LINKS(child);
54 child->parent = child->real_parent;
55 SET_LINKS(child);
56}
57
58
59
60
61int ptrace_check_attach(struct task_struct *child, int kill)
62{
63 if (!(child->ptrace & PT_PTRACED))
64 return -ESRCH;
65
66 if (child->parent != current)
67 return -ESRCH;
68
69 if (!kill) {
70 if (child->state != TASK_STOPPED)
71 return -ESRCH;
72 wait_task_inactive(child);
73 }
74
75
76 return 0;
77}
78
79int ptrace_attach(struct task_struct *task)
80{
81 int retval;
82 task_lock(task);
83 retval = -EPERM;
84 if (task->pid <= 1)
85 goto bad;
86 if (task == current)
87 goto bad;
88 if (!task->mm)
89 goto bad;
90 if(((current->uid != task->euid) ||
91 (current->uid != task->suid) ||
92 (current->uid != task->uid) ||
93 (current->gid != task->egid) ||
94 (current->gid != task->sgid) ||
95 (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
96 goto bad;
97 rmb();
98 if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE))
99 goto bad;
100
101 if (task->ptrace & PT_PTRACED)
102 goto bad;
103 retval = security_ops->ptrace(current, task);
104 if (retval)
105 goto bad;
106
107
108 task->ptrace |= PT_PTRACED;
109 if (capable(CAP_SYS_PTRACE))
110 task->ptrace |= PT_PTRACE_CAP;
111 task_unlock(task);
112
113 write_lock_irq(&tasklist_lock);
114 __ptrace_link(task, current);
115 write_unlock_irq(&tasklist_lock);
116
117 send_sig(SIGSTOP, task, 1);
118 return 0;
119
120bad:
121 task_unlock(task);
122 return retval;
123}
124
125int ptrace_detach(struct task_struct *child, unsigned int data)
126{
127 if ((unsigned long) data > _NSIG)
128 return -EIO;
129
130
131 ptrace_disable(child);
132
133
134 child->exit_code = data;
135
136 write_lock_irq(&tasklist_lock);
137 __ptrace_unlink(child);
138
139 if (child->state != TASK_ZOMBIE)
140 wake_up_process(child);
141 write_unlock_irq(&tasklist_lock);
142
143 return 0;
144}
145
146
147
148
149
150
151
152int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write)
153{
154 struct mm_struct *mm;
155 struct vm_area_struct *vma;
156 struct page *page;
157 void *old_buf = buf;
158
159 mm = get_task_mm(tsk);
160 if (!mm)
161 return 0;
162
163 down_read(&mm->mmap_sem);
164
165 while (len) {
166 int bytes, ret, offset;
167 void *maddr;
168
169 ret = get_user_pages(current, mm, addr, 1,
170 write, 1, &page, &vma);
171 if (ret <= 0)
172 break;
173
174 bytes = len;
175 offset = addr & (PAGE_SIZE-1);
176 if (bytes > PAGE_SIZE-offset)
177 bytes = PAGE_SIZE-offset;
178
179 flush_cache_page(vma, addr);
180
181 maddr = kmap(page);
182 if (write) {
183 memcpy(maddr + offset, buf, bytes);
184 flush_page_to_ram(page);
185 flush_icache_user_range(vma, page, addr, bytes);
186 } else {
187 memcpy(buf, maddr + offset, bytes);
188 flush_page_to_ram(page);
189 }
190 kunmap(page);
191 page_cache_release(page);
192 len -= bytes;
193 buf += bytes;
194 addr += bytes;
195 }
196 up_read(&mm->mmap_sem);
197 mmput(mm);
198
199 return buf - old_buf;
200}
201
202int ptrace_readdata(struct task_struct *tsk, unsigned long src, char *dst, int len)
203{
204 int copied = 0;
205
206 while (len > 0) {
207 char buf[128];
208 int this_len, retval;
209
210 this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
211 retval = access_process_vm(tsk, src, buf, this_len, 0);
212 if (!retval) {
213 if (copied)
214 break;
215 return -EIO;
216 }
217 if (copy_to_user(dst, buf, retval))
218 return -EFAULT;
219 copied += retval;
220 src += retval;
221 dst += retval;
222 len -= retval;
223 }
224 return copied;
225}
226
227int ptrace_writedata(struct task_struct *tsk, char * src, unsigned long dst, int len)
228{
229 int copied = 0;
230
231 while (len > 0) {
232 char buf[128];
233 int this_len, retval;
234
235 this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
236 if (copy_from_user(buf, src, this_len))
237 return -EFAULT;
238 retval = access_process_vm(tsk, dst, buf, this_len, 1);
239 if (!retval) {
240 if (copied)
241 break;
242 return -EIO;
243 }
244 copied += retval;
245 src += retval;
246 dst += retval;
247 len -= retval;
248 }
249 return copied;
250}
251