PTRACE_DETACH fails after PTRACE_CONT with errno=ESRCH

4.1k views Asked by At

In my project I need to attach to processes, resume them and later detach using ptrace. However, detaching fails with errno=ESRCH (No such process).

Detaching works fine if I don't resume the process with PTRACE_CONT, but in this case the process is stopped/unresponsive which is not acceptable in my project. Tested on Arch & Ubuntu 12.04 LTS with identical results.

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ptrace.h>
#include <sys/wait.h>

int main(int argc, char *argv[])
{
    pid_t pid = 21000;

    if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
        perror("PTRACE_ATTACH");
        return 1;
    } 
    printf("attached\n");
    waitpid(pid, NULL, WUNTRACED);

    if (ptrace(PTRACE_CONT, pid, NULL, NULL) == -1) {
        perror("PTRACE_CONT");
        return 1;
    }
    printf("continued\n");

    if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) {
        perror("PTRACE_DETACH");
        return 1;
    }
    printf("detached\n");

    return 0;
}

The output:

attached
continued
PTRACE_DETACH: No such process
1

There are 1 answers

0
betabandido On BEST ANSWER

According to ptrace man page the process should be stopped before trying to detach from it:

Detaching of the tracee is performed by:

ptrace(PTRACE_DETACH, pid, 0, sig);

PTRACE_DETACH is a restarting operation; therefore it requires the tracee to be in ptrace-stop. If the tracee is in signal-delivery- stop, a signal can be injected. Otherwise, the sig parameter may be silently ignored. If the tracee is running when the tracer wants to detach it, the usual solution is to send SIGSTOP (using tgkill(2), to make sure it goes to the correct thread), wait for the tracee to stop in signal- delivery-stop for SIGSTOP and then detach it (suppressing SIGSTOP injection). A design bug is that this can race with concurrent SIGSTOPs. Another complication is that the tracee may enter other ptrace-stops and needs to be restarted and waited for again, until SIGSTOP is seen. Yet another complication is to be sure that the tracee is not already ptrace-stopped, because no signal delivery happens while it is--not even SIGSTOP.

In your example code there is actually no need to call ptrace(PTRACE_CONT, ...). You can just detach from the process. If that code belongs to a larger piece of code, then you can just use tgkill() (or simply kill if you are not using threads):

ptrace(PTRACE_CONT, ...);
kill(pid, SIGSTOP);
waitpid(pid, NULL, 0);
ptrace(PTRACE_DETACH, ...);