进程A中的waitpid()在B coredump

时间:2017-06-19 12:18:13

标签: c multithreading unix signals ptrace

首先,启动流程B(参见下面的mt.cpp),它将创建一个pthread_create()的线程。将为进程A输出主线程的ppidpidtid以及新线程,然后它们都启动for循环并引发SIGTRAP,这应该是在流程A中被waitpid()抓住。

其次,使用进程B的attach.cpp启动进程A(请参阅下面的pid)。进程A将通过ptrace(PTRACE_ATTACH, ...)附加到进程B,然后使用{{1}等待信号事件在waitpid()中,如果获得while(true),请致电ptrace(PTRACE_CONT, ...),如果获得SIGTRAP,请致电循环。

现在是问题所在: 进程A可以捕获进程B的主线程引发的SIGSTOP并成功调用SIGTRAP,然后进程B将继续按预期执行。

BUT !!!

当流程B的新主题提出ptrace(PTRACE_CONT, ...)时,流程A未能SIGTRAP ptrace(PTRACE_CONT, ...) "没有此类流程" ,因为进程B的核心转储了errmsg "跟踪/断点陷阱(核心转储)" 。 此外,errmsg变为假,WIFSTOPPED(status)变为真。

我知道WIFSIGNALED(status)的默认操作是终止进程,似乎SIGTRAP在终止操作之后转移到进程A,而不是之前,因此进程A没有机会继续进程乙

我尝试过SIGTRAP代替进程A,gdb都可以被捕获并继续成功。因此,进程A的代码中肯定存在问题。

以下SIGTRAP作为流程A执行:

attach.cpp

以下#include <sys/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main(int argc, char *argv[]) { pid_t pid = 0; int ret = 0; int status = 0; if (argc > 1) { pid = atoi(argv[1]); printf("pid=%d\n", pid); } ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL); printf("attach ret=%d\n", ret); waitpid(pid, &status, 0); ret = ptrace(PTRACE_CONT, pid, NULL, NULL); printf("cont ret=%d\n", ret); while (true) { ret = waitpid(pid, &status, WUNTRACED); printf("\nwaitpid ret=%d.\n", ret); int sig = 0; if (WIFSIGNALED(status)) { printf("WIFSIGNALED\n"); sig = WTERMSIG(status); } else if (WIFSTOPPED(status)) { printf("WIFSTOPPED\n"); sig = WSTOPSIG(status); } else { printf("other status %d\n", status); } if (SIGTRAP == sig) { ret = ptrace(PTRACE_CONT, pid, NULL, NULL); printf("SIGTRAP cont ret=%d err=%s\n", ret, strerror(errno)); } else if (SIGSTOP == sig) { ret = ptrace(PTRACE_DETACH, pid, NULL, NULL); printf("SIGSTOP detach ret=%d\n", ret); break; } else { printf("other signal %d\n", sig); } sleep(2); } return 0; } 作为流程B执行:

mt.cpp

结果如下:

  • 如果您想自己执行这两个程序,请确保在启动进程B后应尽快启动进程A(attach)。

流程B:

#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/syscall.h>

#define gettid() syscall(SYS_gettid)

void *func(void * arg)
{
  printf("child ppid=%d pid=%d tid=%d\n", getppid(), getpid(), gettid());
  int i = 0;
  for (; i < 5; i++) {
    printf("child loop i=%d\n", i);
    sleep(2);
  }

  printf("\nchild before SIGTRAP\n", gettid());
  raise(SIGTRAP);
  printf("child after SIGTRAP\n\n", gettid());

  for (; i < 8; i++) {
    printf("child loop i=%d\n", i);
    sleep(2);
  }

  return NULL;
}

int main(void)
{
  printf("parent ppid=%d pid=%d tid=%d\n", getppid(), getpid(), gettid());
  pthread_t tid;
  pthread_create(&tid, NULL, func, NULL);

  int i = 0;
  for (; i < 3; i++) {
    printf("parent loop i=%d\n", i);
    sleep(2);
  }

  printf("\nparent before SIGTRAP\n", gettid());
  raise(SIGTRAP);
  printf("parent after SIGTRAP\n\n", gettid());

  for (; i < 10; i++) {
    printf("parent loop i=%d\n", i);
    sleep(2);
  }

  pthread_join(tid, NULL);

  return 0;
}

流程A:

$ ./mt
parent ppid=12238 pid=30389 tid=30389
parent loop i=0
child ppid=12238 pid=30389 tid=30390
child loop i=0
parent loop i=1
child loop i=1
parent loop i=2
child loop i=2

parent before SIGTRAP
child loop i=3
parent after SIGTRAP

parent loop i=3
child loop i=4
parent loop i=4

child before SIGTRAP
Trace/breakpoint trap (core dumped)

1 个答案:

答案 0 :(得分:1)

Linux PTRACE_ATTACH请求,尽管其参数名为 pid ,但只会跟踪该线程。

您可以通过将此函数添加到程序并在两个线程中调用它来验证这一点:

#define trprefix "TracerPid:"

int tracerpid()
{
  char stfile[100], buf[512];
  sprintf(stfile, "/proc/self/task/%d/status", (int)gettid());
  int trpid = -1;
  FILE *st = fopen(stfile, "r");
  if (st != NULL) {
    while (fgets(buf, sizeof buf, st) != NULL) {
      if (strncmp(buf, trprefix, strlen(trprefix)) == 0)
        trpid = atoi(buf+strlen(trprefix));
    }
    fclose(st);
  }
  return trpid;
}

你会看到父线程的Tracer PID是你的“attach”进程,而子线程的Tracer PID是0。

当子线程引发SIGTRAP时,线程没有跟踪器,因此将采用SIGTRAP的默认操作 - 整个过程将被终止。这就是为什么你的追踪者说waitpid返回WIFSIGNALED

解决此问题:

  • 在“mt”程序中,将调用移至pthread_create,使其位于第一个延迟循环之后,这将为您提供足够的时间在创建新线程之前附加到进程。

  • ptrace(PTRACE_ATTACH, ...); waitpid(...);

    之后将其添加到“附加”程序中
    errno = 0;
    ret = ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACECLONE);
    printf("setoptions ret=%d err=%s\n", ret, strerror(errno));
    

    PTRACE_O_TRACECLONE选项将让您的程序跟踪目标使用clone创建的每个线程。

  • 将您的所有waitpid(pid, ...)转换为waitpid(-1, ...),以便您的程序等待任何主题。