Thanks Roland.

But now I am even more confused...

On 09/20, Roland McGrath wrote:
>
> >     - PTRACE_SINGLESTEP does user_enable_single_step()
> >
> >     - when the tracee returns to user mode, the next instruction
> >       causes exception, do_debug()->send_sigtrap() sends SIGTRAP
> >
> >     - the tracee notices the signal and reports this SIGTRAP
>
> Correct.  But if the tracee is inside the syscall path at the time, then
> it's a little different.  TIF_SINGLESTEP being set means it will go to the
> syscall_trace_leave path and send a signal before it actually returns to
> user mode.

I am not sure... I can hardly read entry_64.S, but I am not sure
system_call_fastpath: path calls syscall_trace_leave() on exit...
OK, I added printk to syscall_trace_leave(), please see below.

> > At least. Is it true that X86_EFLAGS_TF should provoke the 
> > exception/do_debug?
>
> Yes.  You can even set it yourself in userland with:
>
>       pushf
>       orl $0x100, (%rsp) /* or (%esp) */
>       popf
>
> and see the signal.

Yes, this works.

> > But, whatever I did this doesn't work. So, what user_enable_single_step()
> > actually means?
>
> It works very well in the vanilla kernel
> ...
> I tried your prctl hack on the vanilla kernel and a test program does
> indeed get a SIGTRAP.  (This is an x86_64 kernel on an Intel chip, trying
> both 32-bit and 64-bit test programs.)

I used the vanilla kernel too. It doesn't work for me. Under KVM at least...

OK, the kernel patch:

        --- TTT/kernel/sys.c~TF_FBG     2009-06-17 14:11:26.000000000 +0200
        +++ TTT/kernel/sys.c    2009-09-21 20:03:27.000000000 +0200
        @@ -1428,6 +1428,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsi
         
                error = 0;
                switch (option) {
        +               case 666:
        +                       printk(KERN_INFO "prctl: before=%lX\n", 
task_pt_regs(current)->flags);
        +                       user_enable_single_step(current);
        +                       printk(KERN_INFO "prctl: after=%lX\n", 
task_pt_regs(current)->flags);
        +                       break;
        +
                        case PR_SET_PDEATHSIG:
                                if (!valid_signal(arg2)) {
                                        error = -EINVAL;
        --- TTT/arch/x86/kernel/ptrace.c~TF_FBG 2009-06-11 14:16:46.000000000 
+0200
        +++ TTT/arch/x86/kernel/ptrace.c        2009-09-21 20:22:52.000000000 
+0200
        @@ -1520,6 +1520,8 @@ asmregparm long syscall_trace_enter(stru
         
         asmregparm void syscall_trace_leave(struct pt_regs *regs)
         {
        +       printk(KERN_INFO "trace_leave enter\n");
        +
                if (unlikely(current->audit_context))
                        audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);
         
        @@ -1543,6 +1545,8 @@ asmregparm void syscall_trace_leave(stru
                 * system call instruction.
                 */
                if (test_thread_flag(TIF_SINGLESTEP) &&
        -           tracehook_consider_fatal_signal(current, SIGTRAP))
        +           tracehook_consider_fatal_signal(current, SIGTRAP)) {
        +               printk(KERN_INFO "trace_leave sends SIGTRAP\n");
                        send_sigtrap(current, regs, 0, TRAP_BRKPT);
        +       }
         }

version:

        $ uname -a
        Linux myhost 2.6.31-00008-g41b61a1-dirty #2 SMP PREEMPT Mon Sep 21 
20:23:05 CEST 2009 x86_64 QEMU Virtual CPU version 0.9.1 GenuineIntel GNU/Linux


The 1ts test:

        int main(void)
        {
                assert(0 == prctl(666));

                for (;;)
                        ;

                return 0;
        }

it doesn't get SIGTRAP, and syscall_trace_leave() is not called.

        $ dmesg -c
        prctl: before=10246
        prctl: after=10346

The same for this test:

        int main(void)
        {
                assert(0 == prctl(666));

                pause();

                return 0;
        }

However,

        int main(void)
        {
                assert(0 == prctl(666));

                printf("after prctl\n");

                return 0;
        }

dies with "Trace/breakpoint trap", dmesg says:

        prctl: before=10246
        prctl: after=10346
        trace_leave enter

I guess "trace_leave enter" corresponds to the next syscall, not to prtcl().

This test

        int main(void)
        {
                printf("before prctl\n");

                assert(0 == prctl(666));

                printf("after prctl\n");

                return 0;
        }

gets SIGTRAP too, but printf() succeeds:

        before prctl
        after prctl
        Trace/breakpoint trap

The final test-case:

        static inline unsigned long getflags(void)
        {
                unsigned long flags;

                asm volatile("pushf ; pop %0\n"
                                : "=rm" (flags)
                                : /* no input */
                                : "memory");
                return flags;
        }

        int main(void)
        {
                unsigned long flags;

                printf("user_enable_single_step()...\n");
                assert(0 == prctl(666));

                flags = getflags();
                printf("flags=%lX\n", flags);

                for (;;)
                        ;

                return 0;
        }

Result:

        user_enable_single_step()...
        flags=246
        Trace/breakpoint trap

dmesg is the same:

        prctl: before=10246
        prctl: after=10346
        trace_leave enter

Oleg.

Reply via email to