【问题标题】:calling ptrace inside a ptraced Linux process在 ptraced Linux 进程中调用 ptrace
【发布时间】:2011-01-22 12:11:11
【问题描述】:

有人添加到Wikipedia "ptrace" article 声称,在 Linux 上,ptraced 进程本身不能 ptrace 另一个进程。我正在尝试确定是否(如果是,为什么)是这种情况。下面是我设计的一个简单程序来测试它。我的程序失败了(子子进程无法正常运行),但我确信这是我的错误,而不是根本问题。

本质上,初始进程 A 派生出进程 B,而后者又派生出 CA ptrace 其子 BB ptrace 其子 C。设置完成后,所有三个进程都将被写入以每秒一次将ABC 打印到标准输出。

实际上,AB 工作正常,但 C 只打印一次然后卡住。检查ps -eo pid,cmd,wchan 显示C 卡在内核函数ptrace_stop 中,而其余的都在hrtimer_nanosleep 中,我希望这三个都在。

偶尔所有三个都可以工作(因此程序会打印 Cs 以及 As 和 Bs),这让我相信在初始设置中存在一些竞争条件。

我的猜测可能是错误的:

  • A 看到与 B 相关的 SIGCHLD 相关,看到 SIGCHLD 与向 C 发出信号有关,并 wait(2) 报告两者都来自 B(但是对两个 pid 的 PTRACE_CONT 的 hacky 调用并不能解决问题)?
  • C 应该由 B 跟踪 - C 继承了 A 的 ptrace(以及 B 对 ptrace 的调用既没有出错也没有覆盖它)?

谁能弄清楚我做错了什么?谢谢。

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

static void a(){
  while(1){
    printf ("A\n");
    fflush(stdout);
    sleep(1);
  }
}

static void b(){
  while(1){
    printf ("B\n");
    fflush(stdout);
    sleep(1);
  }
}

static void c(){
  while(1){
    printf ("C\n");
    fflush(stdout);
    sleep(1);
  }
}

static void sigchld_handler(int sig){
  int result;
  pid_t child_pid = wait(NULL); // find who send us this SIGCHLD

  printf("SIGCHLD on %d\n", child_pid);
  result=ptrace(PTRACE_CONT, child_pid, sig, NULL);
  if(result) {
    perror("continuing after SIGCHLD");
  }
}

int main(int  argc,
         char **argv){

  pid_t mychild_pid;
  int   result;

  printf("pidA = %d\n", getpid());

  signal(SIGCHLD, sigchld_handler);

  mychild_pid = fork();

  if (mychild_pid) {
    printf("pidB = %d\n", mychild_pid);
    result = ptrace(PTRACE_ATTACH, mychild_pid, NULL, NULL);
    if(result==-1){
      perror("outer ptrace");
    }
    a();
  }
  else {
    mychild_pid = fork();

    if (mychild_pid) {
      printf("pidC = %d\n", mychild_pid);

      result = ptrace(PTRACE_ATTACH, mychild_pid, NULL, NULL);
      if(result==-1){
        perror("inner ptrace");
      }
      b();
    }
    else {
      c();
    }
  }

  return 0;
}

【问题讨论】:

  • 不禁注意到ptrace(PTRACE_CONT, child_pid, sig, NULL)...你的意思可能是ptrace(PTRACE_CONT, child_pid, NULL, sig) (data=sig)

标签: c linux ptrace


【解决方案1】:

在调用 ptrace 本身的子进程上执行某些 ptrace 功能是“可能的”。真正的困难在于,一个跟踪器进程在附加到后者时会成为被跟踪者的父进程。如果您的跟踪器进程想要跟踪所有(直接和间接)子进程的所有行为(例如,当调试器程序需要调试多线程程序时),它自然会破坏原始进程层次结构,以及所有进程间/所有子进程之间的线程间通信(即线程同步、信号发送/接收……)需要由跟踪器进程模拟/多路复用。它仍然是“可能的”,但要困难得多且效率低。

【讨论】:

    【解决方案2】:

    您确实看到了竞争条件。您可以通过在 second fork() 调用之前立即放置 sleep(1); 来使其重复发生。

    竞态条件是因为进程 A 没有正确地将信号传递给进程 B。这意味着如果进程 B 在进程 A 开始跟踪进程 B 之后开始跟踪进程 C,则进程 B 永远不会收到 SIGCHLD 信号指示该进程 C 已停止,因此它永远无法继续。

    要解决此问题,您只需修复您的 SIGCHLD 处理程序:

    static void sigchld_handler(int sig){
        int result, status;
        pid_t child_pid = wait(&status); // find who send us this SIGCHLD
    
        printf("%d received SIGCHLD on %d\n", getpid(), child_pid);
        if (WIFSTOPPED(status))
        {
            result=ptrace(PTRACE_CONT, child_pid, 0, WSTOPSIG(status));
            if(result) {
                perror("continuing after SIGCHLD");
            }
        }
    }
    

    【讨论】:

    • 是的,就是这样。非常感谢您可爱而清晰的解释。
    • @caf C ptrace A 有可能吗?
    猜你喜欢
    • 2022-06-14
    • 2014-08-12
    • 1970-01-01
    • 1970-01-01
    • 2020-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-26
    相关资源
    最近更新 更多