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 PTRACE_DETACH and
the tracee gets another trap after resume.

Change utrace_reset() to do user_disable_single_step(). Alternatively
we could change ptrace layer, but I think this is not ptrace specific.

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

 kernel/utrace.c |    1 +
 1 file changed, 1 insertion(+)

--- kstub/kernel/utrace.c~10_utrace_reset_should_clear_ss       2010-09-20 
03:55:12.000000000 +0200
+++ kstub/kernel/utrace.c       2010-09-20 21:33:47.000000000 +0200
@@ -709,6 +709,7 @@ static bool utrace_reset(struct task_str
                 */
                utrace->resume = UTRACE_RESUME;
                utrace->signal_handler = 0;
+               user_disable_single_step(task);
        }
 
        /*

Reply via email to