diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 05:17:24 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 05:17:24 -0400 |
commit | 250c22777fe1ccd7ac588579a6c16db4c0161cc5 (patch) | |
tree | 55c317efb7d792ec6fdae1d1937c67a502c48dec /arch/x86/kernel/ptrace_64.c | |
parent | 2db55d344e529492545cb3b755c7e9ba8e4fa94e (diff) |
x86_64: move kernel
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/ptrace_64.c')
-rw-r--r-- | arch/x86/kernel/ptrace_64.c | 627 |
1 files changed, 627 insertions, 0 deletions
diff --git a/arch/x86/kernel/ptrace_64.c b/arch/x86/kernel/ptrace_64.c new file mode 100644 index 000000000000..eea3702427b4 --- /dev/null +++ b/arch/x86/kernel/ptrace_64.c | |||
@@ -0,0 +1,627 @@ | |||
1 | /* ptrace.c */ | ||
2 | /* By Ross Biro 1/23/92 */ | ||
3 | /* | ||
4 | * Pentium III FXSR, SSE support | ||
5 | * Gareth Hughes <gareth@valinux.com>, May 2000 | ||
6 | * | ||
7 | * x86-64 port 2000-2002 Andi Kleen | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/smp.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/ptrace.h> | ||
16 | #include <linux/user.h> | ||
17 | #include <linux/security.h> | ||
18 | #include <linux/audit.h> | ||
19 | #include <linux/seccomp.h> | ||
20 | #include <linux/signal.h> | ||
21 | |||
22 | #include <asm/uaccess.h> | ||
23 | #include <asm/pgtable.h> | ||
24 | #include <asm/system.h> | ||
25 | #include <asm/processor.h> | ||
26 | #include <asm/i387.h> | ||
27 | #include <asm/debugreg.h> | ||
28 | #include <asm/ldt.h> | ||
29 | #include <asm/desc.h> | ||
30 | #include <asm/proto.h> | ||
31 | #include <asm/ia32.h> | ||
32 | |||
33 | /* | ||
34 | * does not yet catch signals sent when the child dies. | ||
35 | * in exit.c or in signal.c. | ||
36 | */ | ||
37 | |||
38 | /* | ||
39 | * Determines which flags the user has access to [1 = access, 0 = no access]. | ||
40 | * Prohibits changing ID(21), VIP(20), VIF(19), VM(17), IOPL(12-13), IF(9). | ||
41 | * Also masks reserved bits (63-22, 15, 5, 3, 1). | ||
42 | */ | ||
43 | #define FLAG_MASK 0x54dd5UL | ||
44 | |||
45 | /* set's the trap flag. */ | ||
46 | #define TRAP_FLAG 0x100UL | ||
47 | |||
48 | /* | ||
49 | * eflags and offset of eflags on child stack.. | ||
50 | */ | ||
51 | #define EFLAGS offsetof(struct pt_regs, eflags) | ||
52 | #define EFL_OFFSET ((int)(EFLAGS-sizeof(struct pt_regs))) | ||
53 | |||
54 | /* | ||
55 | * this routine will get a word off of the processes privileged stack. | ||
56 | * the offset is how far from the base addr as stored in the TSS. | ||
57 | * this routine assumes that all the privileged stacks are in our | ||
58 | * data space. | ||
59 | */ | ||
60 | static inline unsigned long get_stack_long(struct task_struct *task, int offset) | ||
61 | { | ||
62 | unsigned char *stack; | ||
63 | |||
64 | stack = (unsigned char *)task->thread.rsp0; | ||
65 | stack += offset; | ||
66 | return (*((unsigned long *)stack)); | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * this routine will put a word on the processes privileged stack. | ||
71 | * the offset is how far from the base addr as stored in the TSS. | ||
72 | * this routine assumes that all the privileged stacks are in our | ||
73 | * data space. | ||
74 | */ | ||
75 | static inline long put_stack_long(struct task_struct *task, int offset, | ||
76 | unsigned long data) | ||
77 | { | ||
78 | unsigned char * stack; | ||
79 | |||
80 | stack = (unsigned char *) task->thread.rsp0; | ||
81 | stack += offset; | ||
82 | *(unsigned long *) stack = data; | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | #define LDT_SEGMENT 4 | ||
87 | |||
88 | unsigned long convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs) | ||
89 | { | ||
90 | unsigned long addr, seg; | ||
91 | |||
92 | addr = regs->rip; | ||
93 | seg = regs->cs & 0xffff; | ||
94 | |||
95 | /* | ||
96 | * We'll assume that the code segments in the GDT | ||
97 | * are all zero-based. That is largely true: the | ||
98 | * TLS segments are used for data, and the PNPBIOS | ||
99 | * and APM bios ones we just ignore here. | ||
100 | */ | ||
101 | if (seg & LDT_SEGMENT) { | ||
102 | u32 *desc; | ||
103 | unsigned long base; | ||
104 | |||
105 | seg &= ~7UL; | ||
106 | |||
107 | down(&child->mm->context.sem); | ||
108 | if (unlikely((seg >> 3) >= child->mm->context.size)) | ||
109 | addr = -1L; /* bogus selector, access would fault */ | ||
110 | else { | ||
111 | desc = child->mm->context.ldt + seg; | ||
112 | base = ((desc[0] >> 16) | | ||
113 | ((desc[1] & 0xff) << 16) | | ||
114 | (desc[1] & 0xff000000)); | ||
115 | |||
116 | /* 16-bit code segment? */ | ||
117 | if (!((desc[1] >> 22) & 1)) | ||
118 | addr &= 0xffff; | ||
119 | addr += base; | ||
120 | } | ||
121 | up(&child->mm->context.sem); | ||
122 | } | ||
123 | |||
124 | return addr; | ||
125 | } | ||
126 | |||
127 | static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs) | ||
128 | { | ||
129 | int i, copied; | ||
130 | unsigned char opcode[15]; | ||
131 | unsigned long addr = convert_rip_to_linear(child, regs); | ||
132 | |||
133 | copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0); | ||
134 | for (i = 0; i < copied; i++) { | ||
135 | switch (opcode[i]) { | ||
136 | /* popf and iret */ | ||
137 | case 0x9d: case 0xcf: | ||
138 | return 1; | ||
139 | |||
140 | /* CHECKME: 64 65 */ | ||
141 | |||
142 | /* opcode and address size prefixes */ | ||
143 | case 0x66: case 0x67: | ||
144 | continue; | ||
145 | /* irrelevant prefixes (segment overrides and repeats) */ | ||
146 | case 0x26: case 0x2e: | ||
147 | case 0x36: case 0x3e: | ||
148 | case 0x64: case 0x65: | ||
149 | case 0xf2: case 0xf3: | ||
150 | continue; | ||
151 | |||
152 | case 0x40 ... 0x4f: | ||
153 | if (regs->cs != __USER_CS) | ||
154 | /* 32-bit mode: register increment */ | ||
155 | return 0; | ||
156 | /* 64-bit mode: REX prefix */ | ||
157 | continue; | ||
158 | |||
159 | /* CHECKME: f2, f3 */ | ||
160 | |||
161 | /* | ||
162 | * pushf: NOTE! We should probably not let | ||
163 | * the user see the TF bit being set. But | ||
164 | * it's more pain than it's worth to avoid | ||
165 | * it, and a debugger could emulate this | ||
166 | * all in user space if it _really_ cares. | ||
167 | */ | ||
168 | case 0x9c: | ||
169 | default: | ||
170 | return 0; | ||
171 | } | ||
172 | } | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static void set_singlestep(struct task_struct *child) | ||
177 | { | ||
178 | struct pt_regs *regs = task_pt_regs(child); | ||
179 | |||
180 | /* | ||
181 | * Always set TIF_SINGLESTEP - this guarantees that | ||
182 | * we single-step system calls etc.. This will also | ||
183 | * cause us to set TF when returning to user mode. | ||
184 | */ | ||
185 | set_tsk_thread_flag(child, TIF_SINGLESTEP); | ||
186 | |||
187 | /* | ||
188 | * If TF was already set, don't do anything else | ||
189 | */ | ||
190 | if (regs->eflags & TRAP_FLAG) | ||
191 | return; | ||
192 | |||
193 | /* Set TF on the kernel stack.. */ | ||
194 | regs->eflags |= TRAP_FLAG; | ||
195 | |||
196 | /* | ||
197 | * ..but if TF is changed by the instruction we will trace, | ||
198 | * don't mark it as being "us" that set it, so that we | ||
199 | * won't clear it by hand later. | ||
200 | */ | ||
201 | if (is_setting_trap_flag(child, regs)) | ||
202 | return; | ||
203 | |||
204 | child->ptrace |= PT_DTRACE; | ||
205 | } | ||
206 | |||
207 | static void clear_singlestep(struct task_struct *child) | ||
208 | { | ||
209 | /* Always clear TIF_SINGLESTEP... */ | ||
210 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | ||
211 | |||
212 | /* But touch TF only if it was set by us.. */ | ||
213 | if (child->ptrace & PT_DTRACE) { | ||
214 | struct pt_regs *regs = task_pt_regs(child); | ||
215 | regs->eflags &= ~TRAP_FLAG; | ||
216 | child->ptrace &= ~PT_DTRACE; | ||
217 | } | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * Called by kernel/ptrace.c when detaching.. | ||
222 | * | ||
223 | * Make sure the single step bit is not set. | ||
224 | */ | ||
225 | void ptrace_disable(struct task_struct *child) | ||
226 | { | ||
227 | clear_singlestep(child); | ||
228 | } | ||
229 | |||
230 | static int putreg(struct task_struct *child, | ||
231 | unsigned long regno, unsigned long value) | ||
232 | { | ||
233 | unsigned long tmp; | ||
234 | |||
235 | switch (regno) { | ||
236 | case offsetof(struct user_regs_struct,fs): | ||
237 | if (value && (value & 3) != 3) | ||
238 | return -EIO; | ||
239 | child->thread.fsindex = value & 0xffff; | ||
240 | return 0; | ||
241 | case offsetof(struct user_regs_struct,gs): | ||
242 | if (value && (value & 3) != 3) | ||
243 | return -EIO; | ||
244 | child->thread.gsindex = value & 0xffff; | ||
245 | return 0; | ||
246 | case offsetof(struct user_regs_struct,ds): | ||
247 | if (value && (value & 3) != 3) | ||
248 | return -EIO; | ||
249 | child->thread.ds = value & 0xffff; | ||
250 | return 0; | ||
251 | case offsetof(struct user_regs_struct,es): | ||
252 | if (value && (value & 3) != 3) | ||
253 | return -EIO; | ||
254 | child->thread.es = value & 0xffff; | ||
255 | return 0; | ||
256 | case offsetof(struct user_regs_struct,ss): | ||
257 | if ((value & 3) != 3) | ||
258 | return -EIO; | ||
259 | value &= 0xffff; | ||
260 | return 0; | ||
261 | case offsetof(struct user_regs_struct,fs_base): | ||
262 | if (value >= TASK_SIZE_OF(child)) | ||
263 | return -EIO; | ||
264 | child->thread.fs = value; | ||
265 | return 0; | ||
266 | case offsetof(struct user_regs_struct,gs_base): | ||
267 | if (value >= TASK_SIZE_OF(child)) | ||
268 | return -EIO; | ||
269 | child->thread.gs = value; | ||
270 | return 0; | ||
271 | case offsetof(struct user_regs_struct, eflags): | ||
272 | value &= FLAG_MASK; | ||
273 | tmp = get_stack_long(child, EFL_OFFSET); | ||
274 | tmp &= ~FLAG_MASK; | ||
275 | value |= tmp; | ||
276 | break; | ||
277 | case offsetof(struct user_regs_struct,cs): | ||
278 | if ((value & 3) != 3) | ||
279 | return -EIO; | ||
280 | value &= 0xffff; | ||
281 | break; | ||
282 | } | ||
283 | put_stack_long(child, regno - sizeof(struct pt_regs), value); | ||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | static unsigned long getreg(struct task_struct *child, unsigned long regno) | ||
288 | { | ||
289 | unsigned long val; | ||
290 | switch (regno) { | ||
291 | case offsetof(struct user_regs_struct, fs): | ||
292 | return child->thread.fsindex; | ||
293 | case offsetof(struct user_regs_struct, gs): | ||
294 | return child->thread.gsindex; | ||
295 | case offsetof(struct user_regs_struct, ds): | ||
296 | return child->thread.ds; | ||
297 | case offsetof(struct user_regs_struct, es): | ||
298 | return child->thread.es; | ||
299 | case offsetof(struct user_regs_struct, fs_base): | ||
300 | return child->thread.fs; | ||
301 | case offsetof(struct user_regs_struct, gs_base): | ||
302 | return child->thread.gs; | ||
303 | default: | ||
304 | regno = regno - sizeof(struct pt_regs); | ||
305 | val = get_stack_long(child, regno); | ||
306 | if (test_tsk_thread_flag(child, TIF_IA32)) | ||
307 | val &= 0xffffffff; | ||
308 | return val; | ||
309 | } | ||
310 | |||
311 | } | ||
312 | |||
313 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | ||
314 | { | ||
315 | long i, ret; | ||
316 | unsigned ui; | ||
317 | |||
318 | switch (request) { | ||
319 | /* when I and D space are separate, these will need to be fixed. */ | ||
320 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | ||
321 | case PTRACE_PEEKDATA: | ||
322 | ret = generic_ptrace_peekdata(child, addr, data); | ||
323 | break; | ||
324 | |||
325 | /* read the word at location addr in the USER area. */ | ||
326 | case PTRACE_PEEKUSR: { | ||
327 | unsigned long tmp; | ||
328 | |||
329 | ret = -EIO; | ||
330 | if ((addr & 7) || | ||
331 | addr > sizeof(struct user) - 7) | ||
332 | break; | ||
333 | |||
334 | switch (addr) { | ||
335 | case 0 ... sizeof(struct user_regs_struct) - sizeof(long): | ||
336 | tmp = getreg(child, addr); | ||
337 | break; | ||
338 | case offsetof(struct user, u_debugreg[0]): | ||
339 | tmp = child->thread.debugreg0; | ||
340 | break; | ||
341 | case offsetof(struct user, u_debugreg[1]): | ||
342 | tmp = child->thread.debugreg1; | ||
343 | break; | ||
344 | case offsetof(struct user, u_debugreg[2]): | ||
345 | tmp = child->thread.debugreg2; | ||
346 | break; | ||
347 | case offsetof(struct user, u_debugreg[3]): | ||
348 | tmp = child->thread.debugreg3; | ||
349 | break; | ||
350 | case offsetof(struct user, u_debugreg[6]): | ||
351 | tmp = child->thread.debugreg6; | ||
352 | break; | ||
353 | case offsetof(struct user, u_debugreg[7]): | ||
354 | tmp = child->thread.debugreg7; | ||
355 | break; | ||
356 | default: | ||
357 | tmp = 0; | ||
358 | break; | ||
359 | } | ||
360 | ret = put_user(tmp,(unsigned long __user *) data); | ||
361 | break; | ||
362 | } | ||
363 | |||
364 | /* when I and D space are separate, this will have to be fixed. */ | ||
365 | case PTRACE_POKETEXT: /* write the word at location addr. */ | ||
366 | case PTRACE_POKEDATA: | ||
367 | ret = generic_ptrace_pokedata(child, addr, data); | ||
368 | break; | ||
369 | |||
370 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ | ||
371 | { | ||
372 | int dsize = test_tsk_thread_flag(child, TIF_IA32) ? 3 : 7; | ||
373 | ret = -EIO; | ||
374 | if ((addr & 7) || | ||
375 | addr > sizeof(struct user) - 7) | ||
376 | break; | ||
377 | |||
378 | switch (addr) { | ||
379 | case 0 ... sizeof(struct user_regs_struct) - sizeof(long): | ||
380 | ret = putreg(child, addr, data); | ||
381 | break; | ||
382 | /* Disallows to set a breakpoint into the vsyscall */ | ||
383 | case offsetof(struct user, u_debugreg[0]): | ||
384 | if (data >= TASK_SIZE_OF(child) - dsize) break; | ||
385 | child->thread.debugreg0 = data; | ||
386 | ret = 0; | ||
387 | break; | ||
388 | case offsetof(struct user, u_debugreg[1]): | ||
389 | if (data >= TASK_SIZE_OF(child) - dsize) break; | ||
390 | child->thread.debugreg1 = data; | ||
391 | ret = 0; | ||
392 | break; | ||
393 | case offsetof(struct user, u_debugreg[2]): | ||
394 | if (data >= TASK_SIZE_OF(child) - dsize) break; | ||
395 | child->thread.debugreg2 = data; | ||
396 | ret = 0; | ||
397 | break; | ||
398 | case offsetof(struct user, u_debugreg[3]): | ||
399 | if (data >= TASK_SIZE_OF(child) - dsize) break; | ||
400 | child->thread.debugreg3 = data; | ||
401 | ret = 0; | ||
402 | break; | ||
403 | case offsetof(struct user, u_debugreg[6]): | ||
404 | if (data >> 32) | ||
405 | break; | ||
406 | child->thread.debugreg6 = data; | ||
407 | ret = 0; | ||
408 | break; | ||
409 | case offsetof(struct user, u_debugreg[7]): | ||
410 | /* See arch/i386/kernel/ptrace.c for an explanation of | ||
411 | * this awkward check.*/ | ||
412 | data &= ~DR_CONTROL_RESERVED; | ||
413 | for(i=0; i<4; i++) | ||
414 | if ((0x5554 >> ((data >> (16 + 4*i)) & 0xf)) & 1) | ||
415 | break; | ||
416 | if (i == 4) { | ||
417 | child->thread.debugreg7 = data; | ||
418 | if (data) | ||
419 | set_tsk_thread_flag(child, TIF_DEBUG); | ||
420 | else | ||
421 | clear_tsk_thread_flag(child, TIF_DEBUG); | ||
422 | ret = 0; | ||
423 | } | ||
424 | break; | ||
425 | } | ||
426 | break; | ||
427 | } | ||
428 | case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ | ||
429 | case PTRACE_CONT: /* restart after signal. */ | ||
430 | |||
431 | ret = -EIO; | ||
432 | if (!valid_signal(data)) | ||
433 | break; | ||
434 | if (request == PTRACE_SYSCALL) | ||
435 | set_tsk_thread_flag(child,TIF_SYSCALL_TRACE); | ||
436 | else | ||
437 | clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE); | ||
438 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | ||
439 | child->exit_code = data; | ||
440 | /* make sure the single step bit is not set. */ | ||
441 | clear_singlestep(child); | ||
442 | wake_up_process(child); | ||
443 | ret = 0; | ||
444 | break; | ||
445 | |||
446 | #ifdef CONFIG_IA32_EMULATION | ||
447 | /* This makes only sense with 32bit programs. Allow a | ||
448 | 64bit debugger to fully examine them too. Better | ||
449 | don't use it against 64bit processes, use | ||
450 | PTRACE_ARCH_PRCTL instead. */ | ||
451 | case PTRACE_SET_THREAD_AREA: { | ||
452 | struct user_desc __user *p; | ||
453 | int old; | ||
454 | p = (struct user_desc __user *)data; | ||
455 | get_user(old, &p->entry_number); | ||
456 | put_user(addr, &p->entry_number); | ||
457 | ret = do_set_thread_area(&child->thread, p); | ||
458 | put_user(old, &p->entry_number); | ||
459 | break; | ||
460 | case PTRACE_GET_THREAD_AREA: | ||
461 | p = (struct user_desc __user *)data; | ||
462 | get_user(old, &p->entry_number); | ||
463 | put_user(addr, &p->entry_number); | ||
464 | ret = do_get_thread_area(&child->thread, p); | ||
465 | put_user(old, &p->entry_number); | ||
466 | break; | ||
467 | } | ||
468 | #endif | ||
469 | /* normal 64bit interface to access TLS data. | ||
470 | Works just like arch_prctl, except that the arguments | ||
471 | are reversed. */ | ||
472 | case PTRACE_ARCH_PRCTL: | ||
473 | ret = do_arch_prctl(child, data, addr); | ||
474 | break; | ||
475 | |||
476 | /* | ||
477 | * make the child exit. Best I can do is send it a sigkill. | ||
478 | * perhaps it should be put in the status that it wants to | ||
479 | * exit. | ||
480 | */ | ||
481 | case PTRACE_KILL: | ||
482 | ret = 0; | ||
483 | if (child->exit_state == EXIT_ZOMBIE) /* already dead */ | ||
484 | break; | ||
485 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | ||
486 | child->exit_code = SIGKILL; | ||
487 | /* make sure the single step bit is not set. */ | ||
488 | clear_singlestep(child); | ||
489 | wake_up_process(child); | ||
490 | break; | ||
491 | |||
492 | case PTRACE_SINGLESTEP: /* set the trap flag. */ | ||
493 | ret = -EIO; | ||
494 | if (!valid_signal(data)) | ||
495 | break; | ||
496 | clear_tsk_thread_flag(child,TIF_SYSCALL_TRACE); | ||
497 | set_singlestep(child); | ||
498 | child->exit_code = data; | ||
499 | /* give it a chance to run. */ | ||
500 | wake_up_process(child); | ||
501 | ret = 0; | ||
502 | break; | ||
503 | |||
504 | case PTRACE_DETACH: | ||
505 | /* detach a process that was attached. */ | ||
506 | ret = ptrace_detach(child, data); | ||
507 | break; | ||
508 | |||
509 | case PTRACE_GETREGS: { /* Get all gp regs from the child. */ | ||
510 | if (!access_ok(VERIFY_WRITE, (unsigned __user *)data, | ||
511 | sizeof(struct user_regs_struct))) { | ||
512 | ret = -EIO; | ||
513 | break; | ||
514 | } | ||
515 | ret = 0; | ||
516 | for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) { | ||
517 | ret |= __put_user(getreg(child, ui),(unsigned long __user *) data); | ||
518 | data += sizeof(long); | ||
519 | } | ||
520 | break; | ||
521 | } | ||
522 | |||
523 | case PTRACE_SETREGS: { /* Set all gp regs in the child. */ | ||
524 | unsigned long tmp; | ||
525 | if (!access_ok(VERIFY_READ, (unsigned __user *)data, | ||
526 | sizeof(struct user_regs_struct))) { | ||
527 | ret = -EIO; | ||
528 | break; | ||
529 | } | ||
530 | ret = 0; | ||
531 | for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) { | ||
532 | ret = __get_user(tmp, (unsigned long __user *) data); | ||
533 | if (ret) | ||
534 | break; | ||
535 | ret = putreg(child, ui, tmp); | ||
536 | if (ret) | ||
537 | break; | ||
538 | data += sizeof(long); | ||
539 | } | ||
540 | break; | ||
541 | } | ||
542 | |||
543 | case PTRACE_GETFPREGS: { /* Get the child extended FPU state. */ | ||
544 | if (!access_ok(VERIFY_WRITE, (unsigned __user *)data, | ||
545 | sizeof(struct user_i387_struct))) { | ||
546 | ret = -EIO; | ||
547 | break; | ||
548 | } | ||
549 | ret = get_fpregs((struct user_i387_struct __user *)data, child); | ||
550 | break; | ||
551 | } | ||
552 | |||
553 | case PTRACE_SETFPREGS: { /* Set the child extended FPU state. */ | ||
554 | if (!access_ok(VERIFY_READ, (unsigned __user *)data, | ||
555 | sizeof(struct user_i387_struct))) { | ||
556 | ret = -EIO; | ||
557 | break; | ||
558 | } | ||
559 | set_stopped_child_used_math(child); | ||
560 | ret = set_fpregs(child, (struct user_i387_struct __user *)data); | ||
561 | break; | ||
562 | } | ||
563 | |||
564 | default: | ||
565 | ret = ptrace_request(child, request, addr, data); | ||
566 | break; | ||
567 | } | ||
568 | return ret; | ||
569 | } | ||
570 | |||
571 | static void syscall_trace(struct pt_regs *regs) | ||
572 | { | ||
573 | |||
574 | #if 0 | ||
575 | printk("trace %s rip %lx rsp %lx rax %d origrax %d caller %lx tiflags %x ptrace %x\n", | ||
576 | current->comm, | ||
577 | regs->rip, regs->rsp, regs->rax, regs->orig_rax, __builtin_return_address(0), | ||
578 | current_thread_info()->flags, current->ptrace); | ||
579 | #endif | ||
580 | |||
581 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | ||
582 | ? 0x80 : 0)); | ||
583 | /* | ||
584 | * this isn't the same as continuing with a signal, but it will do | ||
585 | * for normal use. strace only continues with a signal if the | ||
586 | * stopping signal is not SIGTRAP. -brl | ||
587 | */ | ||
588 | if (current->exit_code) { | ||
589 | send_sig(current->exit_code, current, 1); | ||
590 | current->exit_code = 0; | ||
591 | } | ||
592 | } | ||
593 | |||
594 | asmlinkage void syscall_trace_enter(struct pt_regs *regs) | ||
595 | { | ||
596 | /* do the secure computing check first */ | ||
597 | secure_computing(regs->orig_rax); | ||
598 | |||
599 | if (test_thread_flag(TIF_SYSCALL_TRACE) | ||
600 | && (current->ptrace & PT_PTRACED)) | ||
601 | syscall_trace(regs); | ||
602 | |||
603 | if (unlikely(current->audit_context)) { | ||
604 | if (test_thread_flag(TIF_IA32)) { | ||
605 | audit_syscall_entry(AUDIT_ARCH_I386, | ||
606 | regs->orig_rax, | ||
607 | regs->rbx, regs->rcx, | ||
608 | regs->rdx, regs->rsi); | ||
609 | } else { | ||
610 | audit_syscall_entry(AUDIT_ARCH_X86_64, | ||
611 | regs->orig_rax, | ||
612 | regs->rdi, regs->rsi, | ||
613 | regs->rdx, regs->r10); | ||
614 | } | ||
615 | } | ||
616 | } | ||
617 | |||
618 | asmlinkage void syscall_trace_leave(struct pt_regs *regs) | ||
619 | { | ||
620 | if (unlikely(current->audit_context)) | ||
621 | audit_syscall_exit(AUDITSC_RESULT(regs->rax), regs->rax); | ||
622 | |||
623 | if ((test_thread_flag(TIF_SYSCALL_TRACE) | ||
624 | || test_thread_flag(TIF_SINGLESTEP)) | ||
625 | && (current->ptrace & PT_PTRACED)) | ||
626 | syscall_trace(regs); | ||
627 | } | ||