[PATCH 2/3] ptrace: shift user_*_step() from ptrace_resume() to ptrace_stop() path Ignoring the buggy PTRACE_KILL, ptrace_resume() calls user_*_step() when the tracee sleeps in ptrace_stop(). Now that ptrace_resume() sets PT_SINGLE* flags, we can reassign user_*_step() from the tracer to the tracee. Introduce ptrace_finish_stop(), it is called by ptrace_stop() after schedule(). Move user_*_step() call sites from ptrace_resume() to ptrace_finish_stop(). This way: - we can remove user_disable_single_step() from detach paths. This is the main motivation, we can implement asynchronous detach. - this makes the detach-on-exit more correct, we do not leak TIF_SINGLESTEP if the tracer dies. - user_enable_*_step(tsk) can be implemented more efficiently if tsk == current, we can avoid access_process_vm(). Signed-off-by: Oleg Nesterov --- include/linux/ptrace.h | 1 + kernel/signal.c | 1 + kernel/ptrace.c | 16 ++++++++++++---- 3 files changed, 14 insertions(+), 4 deletions(-) --- ptrace/include/linux/ptrace.h~2_ptrace_finish_resume 2011-07-03 21:55:17.000000000 +0200 +++ ptrace/include/linux/ptrace.h 2011-07-03 21:55:37.000000000 +0200 @@ -112,6 +112,7 @@ #include /* For unlikely. */ #include /* For struct task_struct. */ +extern void ptrace_finish_stop(void); extern long arch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data); --- ptrace/kernel/signal.c~2_ptrace_finish_resume 2011-07-03 21:55:17.000000000 +0200 +++ ptrace/kernel/signal.c 2011-07-03 21:55:37.000000000 +0200 @@ -1879,6 +1879,7 @@ static void ptrace_stop(int exit_code, i */ try_to_freeze(); + ptrace_finish_stop(); /* * We are back. Now reacquire the siglock before touching * last_siginfo, so that we are sure to have synchronized with --- ptrace/kernel/ptrace.c~2_ptrace_finish_resume 2011-07-03 21:55:17.000000000 +0200 +++ ptrace/kernel/ptrace.c 2011-07-03 21:55:37.000000000 +0200 @@ -581,6 +581,18 @@ static int ptrace_setsiginfo(struct task #define is_sysemu_singlestep(request) 0 #endif +void ptrace_finish_stop(void) +{ + struct task_struct *task = current; + + if (task->ptrace & PT_BLOCKSTEP) + user_enable_block_step(task); + else if (task->ptrace & PT_SINGLESTEP) + user_enable_single_step(task); + else + user_disable_single_step(task); +} + static int ptrace_resume(struct task_struct *child, long request, unsigned long data) { @@ -604,14 +616,10 @@ static int ptrace_resume(struct task_str if (unlikely(!arch_has_block_step())) return -EIO; child->ptrace |= PT_BLOCKSTEP; - user_enable_block_step(child); } else if (is_singlestep(request) || is_sysemu_singlestep(request)) { if (unlikely(!arch_has_single_step())) return -EIO; child->ptrace |= PT_SINGLESTEP; - user_enable_single_step(child); - } else { - user_disable_single_step(child); } child->exit_code = data;