1 /* ptrace.c */
2 /* By Ross Biro 1/23/92 */
3 /* edited by Linus Torvalds */
4
5 #include <linux/head.h>
6 #include <linux/kernel.h>
7 #include <linux/sched.h>
8 #include <linux/mm.h>
9 #include <errno.h>
10 #include <asm/segment.h>
11 #include <asm/system.h>
12 #include <sys/ptrace.h>
13
14 /*
15 * does not yet catch signals sent when the child dies.
16 * in exit.c or in signal.c.
17 */
18
19 /* determines which flags the user has access to. */
20 /* 1 = access 0 = no access */
21 #define FLAG_MASK 0x00000dd9
22
23 /* set's the trap flag. */
24 #define TRAP_FLAG 0x100
25
26 /*
27 * this is the number to subtract from the top of the stack. To find
28 * the local frame.
29 */
30 #define MAGICNUMBER 68
31
32 void do_no_page(unsigned long, unsigned long, struct task_struct *, unsigned long);
33 void write_verify(unsigned long);
34
35 /* change a pid into a task struct. */
36 static inline int get_task(int pid)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
37 {
38 int i;
39
40 for (i = 0; i < NR_TASKS; i++) {
41 if (task[i] != NULL && (task[i]->pid == pid))
42 return i;
43 }
44 return -1;
45 }
46
47 /*
48 * this routine will get a word off of the processes priviledged stack.
49 * the offset is how far from the base addr as stored in the TSS.
50 * this routine assumes that all the priviledged stacks are in our
51 * data space.
52 */
53 static inline int get_stack_long(struct task_struct *task, int offset)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
54 {
55 unsigned char *stack;
56
57 stack = (unsigned char *)task->tss.esp0;
58 stack += offset;
59 return (*((int *)stack));
60 }
61
62 /*
63 * this routine will put a word on the processes priviledged stack.
64 * the offset is how far from the base addr as stored in the TSS.
65 * this routine assumes that all the priviledged stacks are in our
66 * data space.
67 */
68 static inline int put_stack_long(struct task_struct *task, int offset,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
69 unsigned long data)
70 {
71 unsigned char * stack;
72
73 stack = (unsigned char *) task->tss.esp0;
74 stack += offset;
75 *(unsigned long *) stack = data;
76 return 0;
77 }
78
79 /*
80 * This routine gets a long from any process space by following the page
81 * tables. NOTE! You should check that the long isn't on a page boundary,
82 * and that it is in the task area before calling this: this routine does
83 * no checking.
84 *
85 * NOTE2! This uses "tsk->tss.cr3" even though we know it's currently always
86 * zero. This routine shouldn't have to change when we make a better mm.
87 */
88 static unsigned long get_long(struct task_struct * tsk,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
89 unsigned long addr)
90 {
91 unsigned long page;
92
93 addr += tsk->start_code;
94 repeat:
95 page = tsk->tss.cr3 + ((addr >> 20) & 0xffc);
96 page = *(unsigned long *) page;
97 if (page & PAGE_PRESENT) {
98 page &= 0xfffff000;
99 page += (addr >> 10) & 0xffc;
100 page = *((unsigned long *) page);
101 }
102 if (!(page & PAGE_PRESENT)) {
103 do_no_page(0,addr,tsk,0);
104 goto repeat;
105 }
106 page &= 0xfffff000;
107 page += addr & 0xfff;
108 return *(unsigned long *) page;
109 }
110
111 /*
112 * This routine puts a long into any process space by following the page
113 * tables. NOTE! You should check that the long isn't on a page boundary,
114 * and that it is in the task area before calling this: this routine does
115 * no checking.
116 */
117 static void put_long(struct task_struct * tsk, unsigned long addr,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
118 unsigned long data)
119 {
120 unsigned long page;
121
122 addr += tsk->start_code;
123 repeat:
124 page = tsk->tss.cr3 + ((addr >> 20) & 0xffc);
125 page = *(unsigned long *) page;
126 if (page & PAGE_PRESENT) {
127 page &= 0xfffff000;
128 page += (addr >> 10) & 0xffc;
129 page = *((unsigned long *) page);
130 }
131 if (!(page & PAGE_PRESENT)) {
132 do_no_page(0,addr,tsk,0);
133 goto repeat;
134 }
135 if (!(page & PAGE_RW)) {
136 write_verify(addr);
137 goto repeat;
138 }
139 page &= 0xfffff000;
140 page += addr & 0xfff;
141 *(unsigned long *) page = data;
142 }
143
144 /*
145 * This routine checks the page boundaries, and that the offset is
146 * within the task area. It then calls get_long() to read a long.
147 */
148 static int read_long(struct task_struct * tsk, unsigned long addr,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
149 unsigned long * result)
150 {
151 unsigned long low,high;
152
153 if (addr > TASK_SIZE-4)
154 return -EIO;
155 if ((addr & 0xfff) > PAGE_SIZE-4) {
156 low = get_long(tsk,addr & 0xfffffffc);
157 high = get_long(tsk,(addr+4) & 0xfffffffc);
158 switch (addr & 3) {
159 case 1:
160 low >>= 8;
161 low |= high << 24;
162 break;
163 case 2:
164 low >>= 16;
165 low |= high << 16;
166 break;
167 case 3:
168 low >>= 24;
169 low |= high << 8;
170 break;
171 }
172 *result = low;
173 } else
174 *result = get_long(tsk,addr);
175 return 0;
176 }
177
178 /*
179 * This routine checks the page boundaries, and that the offset is
180 * within the task area. It then calls put_long() to write a long.
181 */
182 static int write_long(struct task_struct * tsk, unsigned long addr,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
183 unsigned long data)
184 {
185 unsigned long low,high;
186
187 if (addr > TASK_SIZE-4)
188 return -EIO;
189 if ((addr & 0xfff) > PAGE_SIZE-4) {
190 low = get_long(tsk,addr & 0xfffffffc);
191 high = get_long(tsk,(addr+4) & 0xfffffffc);
192 switch (addr & 3) {
193 case 0: /* shouldn't happen, but safety first */
194 low = data;
195 break;
196 case 1:
197 low &= 0x000000ff;
198 low |= data << 8;
199 high &= 0xffffff00;
200 high |= data >> 24;
201 break;
202 case 2:
203 low &= 0x0000ffff;
204 low |= data << 16;
205 high &= 0xffff0000;
206 high |= data >> 16;
207 break;
208 case 3:
209 low &= 0x00ffffff;
210 low |= data << 24;
211 high &= 0xff000000;
212 high |= data >> 8;
213 break;
214 }
215 put_long(tsk,addr & 0xfffffffc,low);
216 put_long(tsk,(addr+4) & 0xfffffffc,high);
217 } else
218 put_long(tsk,addr,data);
219 return 0;
220 }
221
222 int sys_ptrace(long request, long pid, long addr, long data)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
223 {
224 struct task_struct *child;
225 int childno;
226
227 if (request == 0) {
228 /* set the ptrace bit in the proccess flags. */
229 current->flags |= PF_PTRACED;
230 return 0;
231 }
232
233 childno = get_task(pid);
234
235 if (childno < 0)
236 return -ESRCH;
237 else
238 child = task[childno];
239
240 if (child->p_pptr != current || !(child->flags & PF_PTRACED) ||
241 child->state != TASK_STOPPED)
242 return -ESRCH;
243
244 switch (request) {
245 /* when I and D space are seperate, these will need to be fixed. */
246 case 1: /* read word at location addr. */
247 case 2: {
248 int tmp,res;
249
250 res = read_long(task[childno], addr, &tmp);
251 if (res < 0)
252 return res;
253 verify_area((void *) data, 4);
254 put_fs_long(tmp,(unsigned long *) data);
255 return 0;
256 }
257
258 /* read the word at location addr in the USER area. */
259 case 3: {
260 int tmp;
261 addr = addr >> 2; /* temporary hack. */
262 if (addr < 0 || addr >= 17)
263 return -EIO;
264 verify_area((void *) data, 4);
265 tmp = get_stack_long(child, 4*addr - MAGICNUMBER);
266 put_fs_long(tmp,(unsigned long *) data);
267 return 0;
268 }
269
270 /* when I and D space are seperate, this will have to be fixed. */
271 case 4: /* write the word at location addr. */
272 case 5:
273 return write_long(task[childno],addr,data);
274
275 case 6: /* write the word at location addr in the USER area */
276 addr = addr >> 2; /* temproary hack. */
277 if (addr < 0 || addr >= 17)
278 return -EIO;
279 if (addr == ORIG_EAX)
280 return -EIO;
281 if (addr == EFL) { /* flags. */
282 data &= FLAG_MASK;
283 data |= get_stack_long(child, EFL*4-MAGICNUMBER) & ~FLAG_MASK;
284 }
285 if (put_stack_long(child, 4*addr-MAGICNUMBER, data))
286 return -EIO;
287 return 0;
288
289 case 7: { /* restart after signal. */
290 long tmp;
291
292 child->signal=0;
293 if (data > 0 && data <= NSIG)
294 child->signal = 1<<(data-1);
295 child->state = 0;
296 /* make sure the single step bit is not set. */
297 tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG;
298 put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
299 return 0;
300 }
301
302 /*
303 * make the child exit. Best I can do is send it a sigkill.
304 * perhaps it should be put in the status that it want's to
305 * exit.
306 */
307 case 8: {
308 long tmp;
309
310 child->state = 0;
311 child->signal = 1 << (SIGKILL-1);
312 /* make sure the single step bit is not set. */
313 tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) & ~TRAP_FLAG;
314 put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
315 return 0;
316 }
317
318 case 9: { /* set the trap flag. */
319 long tmp;
320
321 tmp = get_stack_long(child, 4*EFL-MAGICNUMBER) | TRAP_FLAG;
322 put_stack_long(child, 4*EFL-MAGICNUMBER,tmp);
323 child->state = 0;
324 child->signal = 0;
325 if (data > 0 && data <NSIG)
326 child->signal= 1<<(data-1);
327 /* give it a chance to run. */
328 return 0;
329 }
330
331 default:
332 return -EIO;
333 }
334 }