Ok, the first thing is the cleanup of PTRACE_SYSEMU_SINGLESTEP. I've carefully moved the handling to go near to PTRACE_SINGLESTEP. As said, it's needed also to port this stuff to 2.6.10 easily (wrt the introduction of {clear,set}_singlestep).
The patch is attached both with only my changes, to go on top of the add-SYSEMU_SINGLESTEP one, and in the merged form. They are both for 2.6.9. I'm also going to release both 2.6.9-V8-rc2 and 2.6.10-V8-rc2 with this stuff. I also added a patch about mm->dumpable (still in doubt whether to merge it). Second thing: I've analyzed sysaudit-singlestep-umlhost.... the important thing is #2 below. 1) there is some moving around of do_syscall_trace vars, which I've merged in my cleanup (it's needed for item #3, however it's nice anyway); so I've modified and reattached it. 2) I guess that the ptrace_disable() change (which clears TIF_SYSCALL_TRACE and TIF_SYSCALL_EMU) is just cosmetical. I verified that in kernel/ and arch/i386/kernel the only caller is ptrace_detach (as the comment says), which later clears child->ptrace through __ptrace_unlink. Since everything (I took this for granted) tests current->ptrace | PT_PTRACED, correctness should be ok. It hurts a bit performance, however, because we don't run inside the syscall fastpath, and do one more conditional jump. We must discuss this with mainline, too. 3) a real fix for TIF_SYSCALL_AUDIT | TIF_SINGLESTEP, which is also correct when setting TIF_SYSCALL_EMU too (I had doubts about this, since I was missing that is_singlestep == 1 only for PTRACE_SINGLESTEP and not when doing PTRACE_SYSEMU_SINGLESTEP). I want to merge it before 2.6.11. We must in fact avoid to do the tracing for syscall entry, since TIF_SINGLESTEP does not trigger inside entry.S the syscall tracing (see the marked "testb" line below). (around line 277 of arch/i386/kernel/entry.S): ENTRY(system_call) pushl %eax # save orig_eax SAVE_ALL GET_THREAD_INFO(%ebp) # system call tracing in operation / emulation //in the mask _TIF_SINGLESTEP is not set !!! <<<<<<<<<<<<<< testb $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE| _TIF_SYSCALL_AUDIT),TI_flags(%ebp) jnz syscall_trace_entry cmpl $(nr_syscalls), %eax jae syscall_badsys syscall_call: call *sys_call_table(,%eax,4) movl %eax,EAX(%esp) # store the return value syscall_exit: -- Paolo Giarrusso, aka Blaisorblade Linux registered user n. 292729 http://www.user-mode-linux.org/~blaisorblade
1) Move around a couple of vars as done in the other Bodo's cleanup, which requires it. Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[EMAIL PROTECTED]> --- vanilla-linux-2.6.9-paolo/arch/i386/kernel/ptrace.c | 39 ++++++++------------ 1 files changed, 17 insertions(+), 22 deletions(-) diff -puN arch/i386/kernel/ptrace.c~skas-SYSEMU_SINGLESTEP-cleanup arch/i386/kernel/ptrace.c --- vanilla-linux-2.6.9/arch/i386/kernel/ptrace.c~skas-SYSEMU_SINGLESTEP-cleanup 2005-02-10 18:03:36.385950040 +0100 +++ vanilla-linux-2.6.9-paolo/arch/i386/kernel/ptrace.c 2005-02-10 18:13:21.860944312 +0100 @@ -360,7 +360,6 @@ asmlinkage int sys_ptrace(long request, break; case PTRACE_SYSEMU: /* continue and stop at next syscall, which will not be executed */ - case PTRACE_SYSEMU_SINGLESTEP: /* Same as SYSEMU, but singlestep if not syscall */ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: { /* restart after signal. */ long tmp; @@ -368,31 +367,20 @@ asmlinkage int sys_ptrace(long request, ret = -EIO; if ((unsigned long) data > _NSIG) break; - /* prepare to reset single step bit */ - tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; if (request == PTRACE_SYSEMU) { set_tsk_thread_flag(child, TIF_SYSCALL_EMU); clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - clear_tsk_thread_flag(child, TIF_SINGLESTEP); - } - else if (request == PTRACE_SYSEMU_SINGLESTEP) { - set_tsk_thread_flag(child, TIF_SYSCALL_EMU); - set_tsk_thread_flag(child, TIF_SINGLESTEP); - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - /* For SYSEMU_SINGLESTEP, set single step bit */ - tmp |= TRAP_FLAG; - } - else if (request == PTRACE_SYSCALL) { + } else if (request == PTRACE_SYSCALL) { set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); - clear_tsk_thread_flag(child, TIF_SINGLESTEP); - } - else { + } else { clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - clear_tsk_thread_flag(child, TIF_SINGLESTEP); } + clear_tsk_thread_flag(child, TIF_SINGLESTEP); child->exit_code = data; + /* make sure the single step bit is not set. */ + tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; put_stack_long(child, EFL_OFFSET,tmp); wake_up_process(child); ret = 0; @@ -419,14 +407,21 @@ asmlinkage int sys_ptrace(long request, break; } + case PTRACE_SYSEMU_SINGLESTEP: /* Same as SYSEMU, but singlestep if not syscall */ case PTRACE_SINGLESTEP: { /* set the trap flag. */ long tmp; ret = -EIO; if ((unsigned long) data > _NSIG) break; - clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); + + if (request == PTRACE_SYSEMU_SINGLESTEP) + set_tsk_thread_flag(child, TIF_SYSCALL_EMU); + else + clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + if ((child->ptrace & PT_DTRACE) == 0) { /* Spurious delayed TF traps may occur */ child->ptrace |= PT_DTRACE; @@ -601,7 +596,10 @@ out: __attribute__((regparm(3))) int do_syscall_trace(struct pt_regs *regs, int entryexit) { - int is_sysemu, is_singlestep; + int is_sysemu = test_thread_flag(TIF_SYSCALL_EMU); + /* With TIF_SYSCALL_EMU set we want to ignore TIF_SINGLESTEP */ + int is_singlestep = !is_sysemu && test_thread_flag(TIF_SINGLESTEP); + if (unlikely(current->audit_context)) { if (!entryexit) audit_syscall_entry(current, regs->orig_eax, @@ -610,14 +608,11 @@ int do_syscall_trace(struct pt_regs *reg else audit_syscall_exit(current, regs->eax); } - is_sysemu = test_thread_flag(TIF_SYSCALL_EMU); /* If a process stops on the 1st tracepoint with SYSCALL_TRACE * and then is resumed with SYSEMU_SINGLESTEP, it will come in * here. We have to check this and return */ if (is_sysemu && entryexit) return 0; - /* With TIF_SYSCALL_EMU set we want to ignore TIF_SINGLESTEP */ - is_singlestep = !is_sysemu && test_thread_flag(TIF_SINGLESTEP); if (!test_thread_flag(TIF_SYSCALL_TRACE) && !is_singlestep && !is_sysemu) _
From: Bodo Stroesser <[EMAIL PROTECTED]> This patch is based on 2.6.9-vanilla + host-skas3-2.6.9-v7.patch + patch-2.6.9-skas-v7-reorganize It implements the new ptrace option PTRACE_SYSEMU_SINGLESTEP this new option can be used by UML to singlestep a process. Then it will receive the common syscall interceptions plus a singlestep trap for each non syscall instruction. Signed-off-by: Bodo Stroesser <[EMAIL PROTECTED]> Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[EMAIL PROTECTED]> --- vanilla-linux-2.6.9-paolo/arch/i386/kernel/ptrace.c | 28 ++++++++++++++------ vanilla-linux-2.6.9-paolo/include/linux/ptrace.h | 1 2 files changed, 21 insertions(+), 8 deletions(-) diff -puN arch/i386/kernel/ptrace.c~skas-add-SYSEMU_SINGLESTEP arch/i386/kernel/ptrace.c --- vanilla-linux-2.6.9/arch/i386/kernel/ptrace.c~skas-add-SYSEMU_SINGLESTEP 2005-02-10 19:49:52.577621568 +0100 +++ vanilla-linux-2.6.9-paolo/arch/i386/kernel/ptrace.c 2005-02-10 19:50:05.294688280 +0100 @@ -370,12 +370,10 @@ asmlinkage int sys_ptrace(long request, if (request == PTRACE_SYSEMU) { set_tsk_thread_flag(child, TIF_SYSCALL_EMU); clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - } - else if (request == PTRACE_SYSCALL) { + } else if (request == PTRACE_SYSCALL) { set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); - } - else { + } else { clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); } @@ -409,14 +407,21 @@ asmlinkage int sys_ptrace(long request, break; } + case PTRACE_SYSEMU_SINGLESTEP: /* Same as SYSEMU, but singlestep if not syscall */ case PTRACE_SINGLESTEP: { /* set the trap flag. */ long tmp; ret = -EIO; if ((unsigned long) data > _NSIG) break; - clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); + + if (request == PTRACE_SYSEMU_SINGLESTEP) + set_tsk_thread_flag(child, TIF_SYSCALL_EMU); + else + clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + if ((child->ptrace & PT_DTRACE) == 0) { /* Spurious delayed TF traps may occur */ child->ptrace |= PT_DTRACE; @@ -591,7 +596,10 @@ out: __attribute__((regparm(3))) int do_syscall_trace(struct pt_regs *regs, int entryexit) { - int is_sysemu, is_singlestep; + int is_sysemu = test_thread_flag(TIF_SYSCALL_EMU); + /* With TIF_SYSCALL_EMU set we want to ignore TIF_SINGLESTEP */ + int is_singlestep = !is_sysemu && test_thread_flag(TIF_SINGLESTEP); + if (unlikely(current->audit_context)) { if (!entryexit) audit_syscall_entry(current, regs->orig_eax, @@ -600,8 +608,11 @@ int do_syscall_trace(struct pt_regs *reg else audit_syscall_exit(current, regs->eax); } - is_sysemu = test_thread_flag(TIF_SYSCALL_EMU); - is_singlestep = test_thread_flag(TIF_SINGLESTEP); + /* If a process stops on the 1st tracepoint with SYSCALL_TRACE + * and then is resumed with SYSEMU_SINGLESTEP, it will come in + * here. We have to check this and return */ + if (is_sysemu && entryexit) + return 0; if (!test_thread_flag(TIF_SYSCALL_TRACE) && !is_singlestep && !is_sysemu) @@ -626,6 +637,7 @@ int do_syscall_trace(struct pt_regs *reg if ( !is_sysemu ) return 0; + regs->orig_eax = -1; /* force skip of syscall restarting */ if (unlikely(current->audit_context)) audit_syscall_exit(current, regs->eax); return 1; diff -puN include/linux/ptrace.h~skas-add-SYSEMU_SINGLESTEP include/linux/ptrace.h --- vanilla-linux-2.6.9/include/linux/ptrace.h~skas-add-SYSEMU_SINGLESTEP 2005-02-10 19:49:52.579621264 +0100 +++ vanilla-linux-2.6.9-paolo/include/linux/ptrace.h 2005-02-10 19:49:52.582620808 +0100 @@ -21,6 +21,7 @@ #define PTRACE_SYSCALL 24 #define PTRACE_SYSEMU 31 +#define PTRACE_SYSEMU_SINGLESTEP 32 /* 0x4200-0x4300 are reserved for architecture-independent additions. */ #define PTRACE_SETOPTIONS 0x4200 _
From: Bodo Stroesser <[EMAIL PROTECTED]>, Paolo 'Blaisorblade' Giarrusso <[EMAIL PROTECTED]> CC: Roland McGrath <[EMAIL PROTECTED]> This patch applies on top of SKAS/SYSEMU patches, however the bug it fixes is of general relevance: /* With TIF_SYSCALL_AUDIT | TIF_SINGLESTEP we * come in here, but must not continue with * ptrace_notify() In fact, we must avoid to do the tracing for syscall entry, since TIF_SINGLESTEP does not trigger inside entry.S the syscall tracing (see the testb line below): (around line 277 of arch/i386/kernel/entry.S): ENTRY(system_call) pushl %eax # save orig_eax SAVE_ALL GET_THREAD_INFO(%ebp) # system call tracing in operation / emulation #in the mask _TIF_SINGLESTEP is not set !!! <<<<<<<<<<<<<< testb $(_TIF_SYSCALL_EMU|_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_flags(%ebp) jnz syscall_trace_entry cmpl $(nr_syscalls), %eax jae syscall_badsys syscall_call: call *sys_call_table(,%eax,4) movl %eax,EAX(%esp) # store the return value syscall_exit: So, it means that auditing a SINGLESTEP'ed process causes the tracer to get one more trap on the syscall entry path, beyond the one on the syscall exit path. Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[EMAIL PROTECTED]> --- vanilla-linux-2.6.9-paolo/arch/i386/kernel/ptrace.c | 14 +++++++++++++- 1 files changed, 13 insertions(+), 1 deletion(-) diff -puN arch/i386/kernel/ptrace.c~sysaudit-singlestep-umlhost arch/i386/kernel/ptrace.c --- vanilla-linux-2.6.9/arch/i386/kernel/ptrace.c~sysaudit-singlestep-umlhost 2005-02-10 18:15:41.262752016 +0100 +++ vanilla-linux-2.6.9-paolo/arch/i386/kernel/ptrace.c 2005-02-10 18:38:46.874107072 +0100 @@ -151,6 +151,8 @@ void ptrace_disable(struct task_struct * clear_tsk_thread_flag(child, TIF_SINGLESTEP); tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG; put_stack_long(child, EFL_OFFSET, tmp); + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); } /* @@ -601,10 +603,20 @@ int do_syscall_trace(struct pt_regs *reg int is_singlestep = !is_sysemu && test_thread_flag(TIF_SINGLESTEP); if (unlikely(current->audit_context)) { - if (!entryexit) + if (!entryexit) { audit_syscall_entry(current, regs->orig_eax, regs->ebx, regs->ecx, regs->edx, regs->esi); + /* With TIF_SYSCALL_AUDIT | TIF_SINGLESTEP && + * !TIF_SYSCALL_EMU we come in here, but must not + * continue with ptrace_notify(). + * In the SINGLESTEP && ! _AUDIT case (i.e. normal one), + * entry.S will call us only on syscall exit and not on + * the syscall entry path, so let's be consistent. + */ + if (is_singlestep) + return 0; + } else audit_syscall_exit(current, regs->eax); } _