diff options
Diffstat (limited to 'arch/i386/kernel/ptrace.c')
-rw-r--r-- | arch/i386/kernel/ptrace.c | 57 |
1 files changed, 39 insertions, 18 deletions
diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index 5ee9e1d60653..5b569dc1c227 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))) |
681 | void do_syscall_trace(struct pt_regs *regs, int entryexit) | 695 | int 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 | } |