aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/kernel/ptrace.c
diff options
context:
space:
mode:
authorLaurent Vivier <LaurentVivier@wanadoo.fr>2005-09-03 18:57:18 -0400
committerLinus Torvalds <torvalds@evo.osdl.org>2005-09-05 03:06:20 -0400
commited75e8d58010fdc06e2c3a81bfbebae92314c7e3 (patch)
tree3f6f8dc5a34c9e03f613d4b907e02802ab075a9e /arch/i386/kernel/ptrace.c
parent94c80b2598dbd2b8a6fe5f5c2c3af1beb37f66c7 (diff)
[PATCH] UML Support - Ptrace: adds the host SYSEMU support, for UML and general usage
Jeff Dike <jdike@addtoit.com>, Paolo 'Blaisorblade' Giarrusso <blaisorblade_spam@yahoo.it>, Bodo Stroesser <bstroesser@fujitsu-siemens.com> Adds a new ptrace(2) mode, called PTRACE_SYSEMU, resembling PTRACE_SYSCALL except that the kernel does not execute the requested syscall; this is useful to improve performance for virtual environments, like UML, which want to run the syscall on their own. In fact, using PTRACE_SYSCALL means stopping child execution twice, on entry and on exit, and each time you also have two context switches; with SYSEMU you avoid the 2nd stop and so save two context switches per syscall. Also, some architectures don't have support in the host for changing the syscall number via ptrace(), which is currently needed to skip syscall execution (UML turns any syscall into getpid() to avoid it being executed on the host). Fixing that is hard, while SYSEMU is easier to implement. * This version of the patch includes some suggestions of Jeff Dike to avoid adding any instructions to the syscall fast path, plus some other little changes, by myself, to make it work even when the syscall is executed with SYSENTER (but I'm unsure about them). It has been widely tested for quite a lot of time. * Various fixed were included to handle the various switches between various states, i.e. when for instance a syscall entry is traced with one of PT_SYSCALL / _SYSEMU / _SINGLESTEP and another one is used on exit. Basically, this is done by remembering which one of them was used even after the call to ptrace_notify(). * We're combining TIF_SYSCALL_EMU with TIF_SYSCALL_TRACE or TIF_SINGLESTEP to make do_syscall_trace() notice that the current syscall was started with SYSEMU on entry, so that no notification ought to be done in the exit path; this is a bit of a hack, so this problem is solved in another way in next patches. * Also, the effects of the patch: "Ptrace - i386: fix Syscall Audit interaction with singlestep" are cancelled; they are restored back in the last patch of this series. Detailed descriptions of the patches doing this kind of processing follow (but I've already summed everything up). * Fix behaviour when changing interception kind #1. In do_syscall_trace(), we check the status of the TIF_SYSCALL_EMU flag only after doing the debugger notification; but the debugger might have changed the status of this flag because he continued execution with PTRACE_SYSCALL, so this is wrong. This patch fixes it by saving the flag status before calling ptrace_notify(). * Fix behaviour when changing interception kind #2: avoid intercepting syscall on return when using SYSCALL again. A guest process switching from using PTRACE_SYSEMU to PTRACE_SYSCALL crashes. The problem is in arch/i386/kernel/entry.S. The current SYSEMU patch inhibits the syscall-handler to be called, but does not prevent do_syscall_trace() to be called after this for syscall completion interception. The appended patch fixes this. It reuses the flag TIF_SYSCALL_EMU to remember "we come from PTRACE_SYSEMU and now are in PTRACE_SYSCALL", since the flag is unused in the depicted situation. * Fix behaviour when changing interception kind #3: avoid intercepting syscall on return when using SINGLESTEP. When testing 2.6.9 and the skas3.v6 patch, with my latest patch and had problems with singlestepping on UML in SKAS with SYSEMU. It looped receiving SIGTRAPs without moving forward. EIP of the traced process was the same for all SIGTRAPs. What's missing is to handle switching from PTRACE_SYSCALL_EMU to PTRACE_SINGLESTEP in a way very similar to what is done for the change from PTRACE_SYSCALL_EMU to PTRACE_SYSCALL_TRACE. I.e., after calling ptrace(PTRACE_SYSEMU), on the return path, the debugger is notified and then wake ups the process; the syscall is executed (or skipped, when do_syscall_trace() returns 0, i.e. when using PTRACE_SYSEMU), and do_syscall_trace() is called again. Since we are on the return path of a SYSEMU'd syscall, if the wake up is performed through ptrace(PTRACE_SYSCALL), we must still avoid notifying the parent of the syscall exit. Now, this behaviour is extended even to resuming with PTRACE_SINGLESTEP. Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> Cc: Jeff Dike <jdike@addtoit.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel/ptrace.c')
-rw-r--r--arch/i386/kernel/ptrace.c57
1 files changed, 39 insertions, 18 deletions
diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c
index 5ee9e1d6065..5b569dc1c22 100644
--- a/arch/i386/kernel/ptrace.c
+++ b/arch/i386/kernel/ptrace.c
@@ -509,15 +509,27 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
509 } 509 }
510 break; 510 break;
511 511
512 case PTRACE_SYSEMU: /* continue and stop at next syscall, which will not be executed */
512 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ 513 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
513 case PTRACE_CONT: /* restart after signal. */ 514 case PTRACE_CONT: /* restart after signal. */
514 ret = -EIO; 515 ret = -EIO;
515 if (!valid_signal(data)) 516 if (!valid_signal(data))
516 break; 517 break;
518 /* If we came here with PTRACE_SYSEMU and now continue with
519 * PTRACE_SYSCALL, entry.S used to intercept the syscall return.
520 * But it shouldn't!
521 * So we don't clear TIF_SYSCALL_EMU, which is always unused in
522 * this special case, to remember, we came from SYSEMU. That
523 * flag will be cleared by do_syscall_trace().
524 */
525 if (request == PTRACE_SYSEMU) {
526 set_tsk_thread_flag(child, TIF_SYSCALL_EMU);
527 } else if (request == PTRACE_CONT) {
528 clear_tsk_thread_flag(child, TIF_SYSCALL_EMU);
529 }
517 if (request == PTRACE_SYSCALL) { 530 if (request == PTRACE_SYSCALL) {
518 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 531 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
519 } 532 } else {
520 else {
521 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 533 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
522 } 534 }
523 child->exit_code = data; 535 child->exit_code = data;
@@ -546,6 +558,8 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
546 ret = -EIO; 558 ret = -EIO;
547 if (!valid_signal(data)) 559 if (!valid_signal(data))
548 break; 560 break;
561 /*See do_syscall_trace to know why we don't clear
562 * TIF_SYSCALL_EMU.*/
549 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 563 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
550 set_singlestep(child); 564 set_singlestep(child);
551 child->exit_code = data; 565 child->exit_code = data;
@@ -678,37 +692,43 @@ void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
678 * - triggered by current->work.syscall_trace 692 * - triggered by current->work.syscall_trace
679 */ 693 */
680__attribute__((regparm(3))) 694__attribute__((regparm(3)))
681void do_syscall_trace(struct pt_regs *regs, int entryexit) 695int do_syscall_trace(struct pt_regs *regs, int entryexit)
682{ 696{
697 int is_sysemu, is_systrace, is_singlestep, ret = 0;
683 /* do the secure computing check first */ 698 /* do the secure computing check first */
684 secure_computing(regs->orig_eax); 699 secure_computing(regs->orig_eax);
685 700
686 if (unlikely(current->audit_context)) { 701 if (unlikely(current->audit_context) && entryexit)
687 if (entryexit) 702 audit_syscall_exit(current, AUDITSC_RESULT(regs->eax), regs->eax);
688 audit_syscall_exit(current, AUDITSC_RESULT(regs->eax), regs->eax);
689
690 /* Debug traps, when using PTRACE_SINGLESTEP, must be sent only
691 * on the syscall exit path. Normally, when TIF_SYSCALL_AUDIT is
692 * not used, entry.S will call us only on syscall exit, not
693 * entry ; so when TIF_SYSCALL_AUDIT is used we must avoid
694 * calling send_sigtrap() on syscall entry.
695 */
696 else if (is_singlestep)
697 goto out;
698 }
699 703
700 if (!(current->ptrace & PT_PTRACED)) 704 if (!(current->ptrace & PT_PTRACED))
701 goto out; 705 goto out;
702 706
707 is_sysemu = test_thread_flag(TIF_SYSCALL_EMU);
708 is_systrace = test_thread_flag(TIF_SYSCALL_TRACE);
709 is_singlestep = test_thread_flag(TIF_SINGLESTEP);
710
711 /* We can detect the case of coming from PTRACE_SYSEMU and now running
712 * with PTRACE_SYSCALL or PTRACE_SINGLESTEP, by TIF_SYSCALL_EMU being
713 * set additionally.
714 * If so let's reset the flag and return without action (no singlestep
715 * nor syscall tracing, since no actual step has been executed).
716 */
717 if (is_sysemu && (is_systrace || is_singlestep)) {
718 clear_thread_flag(TIF_SYSCALL_EMU);
719 goto out;
720 }
721
703 /* Fake a debug trap */ 722 /* Fake a debug trap */
704 if (test_thread_flag(TIF_SINGLESTEP)) 723 if (test_thread_flag(TIF_SINGLESTEP))
705 send_sigtrap(current, regs, 0); 724 send_sigtrap(current, regs, 0);
706 725
707 if (!test_thread_flag(TIF_SYSCALL_TRACE)) 726 if (!is_systrace && !is_sysemu)
708 goto out; 727 goto out;
709 728
710 /* the 0x80 provides a way for the tracing parent to distinguish 729 /* the 0x80 provides a way for the tracing parent to distinguish
711 between a syscall stop and SIGTRAP delivery */ 730 between a syscall stop and SIGTRAP delivery */
731 /* Note that the debugger could change the result of test_thread_flag!*/
712 ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); 732 ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
713 733
714 /* 734 /*
@@ -720,9 +740,10 @@ void do_syscall_trace(struct pt_regs *regs, int entryexit)
720 send_sig(current->exit_code, current, 1); 740 send_sig(current->exit_code, current, 1);
721 current->exit_code = 0; 741 current->exit_code = 0;
722 } 742 }
743 ret = is_sysemu;
723 out: 744 out:
724 if (unlikely(current->audit_context) && !entryexit) 745 if (unlikely(current->audit_context) && !entryexit)
725 audit_syscall_entry(current, AUDIT_ARCH_I386, regs->orig_eax, 746 audit_syscall_entry(current, AUDIT_ARCH_I386, regs->orig_eax,
726 regs->ebx, regs->ecx, regs->edx, regs->esi); 747 regs->ebx, regs->ecx, regs->edx, regs->esi);
727 748 return ret;
728} 749}