Test-case:

        #include <stdio.h>
        #include <signal.h>
        #include <unistd.h>
        #include <sys/ptrace.h>
        #include <sys/wait.h>
        #include <assert.h>

        int main(void)
        {
                int pid, status;

                pid = fork();
                if (!pid) {
                        assert(ptrace(PTRACE_TRACEME, 0,0,0) == 0);
                        kill(getpid(), SIGSTOP);
                        return 0x23;
                }

                assert(wait(&status) == pid);
                assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP);

                assert(ptrace(PTRACE_SINGLESTEP, pid, 0,0) == 0);
                assert(wait(&status) == pid);
                assert(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP);

                assert(ptrace(PTRACE_DETACH, pid, 0,0) == 0);
                assert(wait(&status) == pid);

                if (WIFEXITED(status) && WEXITSTATUS(status) == 0x23)
                        return 0;

                printf("ERR!! exit status: %04x\n", status);
                return 1;
        }

It fails because TIF_SINGLESTEP is not cleared after UTRACE_DETACH and
the tracee gets another trap after resume.

Change utrace_reset() to do user_disable_single_step() if we are going
to wake up the tracee without another report.

This also cleanups the code, we can kill the similar logic in
utrace_control(UTRACE_RESUME).

Signed-off-by: Oleg Nesterov <o...@redhat.com>
---

 kernel/utrace.c |   16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

--- kstub/kernel/utrace.c~10_utrace_reset_should_clear_ss       2010-10-12 
21:19:33.000000000 +0200
+++ kstub/kernel/utrace.c       2010-10-20 18:18:40.000000000 +0200
@@ -714,8 +714,15 @@ static bool utrace_reset(struct task_str
        /*
         * If no more engines want it stopped, wake it up.
         */
-       if (task_is_traced(task) && !(flags & ENGINE_STOP))
+       if (task_is_traced(task) && !(flags & ENGINE_STOP)) {
+               /*
+                * It just resumes, so make sure single-step
+                * is not left set.
+                */
+               if (utrace->resume == UTRACE_RESUME)
+                       user_disable_single_step(task);
                utrace_wakeup(task, utrace);
+       }
 
        /*
         * In theory spin_lock() doesn't imply rcu_read_lock().
@@ -1155,14 +1162,7 @@ int utrace_control(struct task_struct *t
                break;
 
        case UTRACE_RESUME:
-               /*
-                * This and all other cases imply resuming if stopped.
-                * There might not be another report before it just
-                * resumes, so make sure single-step is not left set.
-                */
                clear_engine_wants_stop(engine);
-               if (likely(reset))
-                       user_disable_single_step(target);
                break;
 
        case UTRACE_BLOCKSTEP:

Reply via email to