strace 如何监控自身?

Pre*_*rem 5 kernel monitoring recursive strace

我有一个假设的情况:

  1. 假设我们有两个 strace 进程 S1 和 S2,它们只是相互监视。
    这怎么可能?
    好吧,在 strace 的命令行选项中,-p PID是传递所需 PID 的方式,当我们发出 strace 命令时,它(在我们的例子中)还不知道。我们可以更改 strace 源代码,这-P 0意味着向用户询问 PID。例如,来自 STDIN 的 read()。当我们可以在两个 shell 会话中运行两个 strace 进程并在第三个 shell 中找到 PID 时,我们可以将该输入提供给 S1 和 S2 并让它们相互监视。
    S1 和 S2 会卡住吗?或者,进入无限循环,或立即崩溃或...?

  2. 再一次,假设我们有另一个 strace 进程 S3,使用-p -1,通过修改源代码,我们用它来告诉 S3 监视自己。例如,使用 getpid() 而不使用 STDIN。 S3会崩溃吗?或者,它会挂起而无法进一步处理吗?它会等待某个事件发生,但是,因为它在等待,所以不会发生任何事件吗?

在 strace 手册页中,它说我们无法监视 init 进程。是否有任何其他限制由 strace 或内核强制执行,以避免循环依赖或循环?

一些特殊情况:
S4 监控 S5,S5 监控 S6,S6 监控 S4。
S7 和 S8 相互监视,其中 S7 是 S8 的父级。
更多的特殊情况是可能的。

编辑(@Ralph Rönnquist & @pfnuesel 评论后):https :
//github.com/bnoordhuis/strace/blob/master/strace.c#L941

if (pid <= 0) {
    error_msg_and_die("Invalid process id: '%s'", opt);
}
if (pid == strace_tracer_pid) {
    error_msg_and_die("I'm sorry, I can't let you do that, Dave.");
}
Run Code Online (Sandbox Code Playgroud)

具体来说,如果strace.c不检查pid == strace_tracer_pid或任何其他特殊情况会发生什么?对一个进程监控本身是否有任何技术限制(在内核中)?一组 2 个(或 3 个或更多)进程监控自己怎么样?系统会崩溃或挂起吗?

小智 7

% sh -c 'exec strace -p $$'    
strace: I'm sorry, I can't let you do that, Dave.
Run Code Online (Sandbox Code Playgroud)

:-)


Bri*_* Bi 5

我只回答Linux。

\n\n

令人惊讶的是,在较新的内核中,为了实际执行跟踪而ptrace使用的系统调用被允许跟踪 init 进程。strace手册页说:

\n\n
   EPERM  The specified process cannot be traced.  This could  be  because\n          the  tracer has insufficient privileges (the required capability\n          is CAP_SYS_PTRACE); unprivileged  processes  cannot  trace  pro\xe2\x80\x90\n          cesses  that  they  cannot send signals to or those running set-\n          user-ID/set-group-ID programs, for  obvious  reasons.   Alterna\xe2\x80\x90\n          tively,  the process may already be being traced, or (on kernels\n          before 2.6.26) be init(8) (PID 1).\n
Run Code Online (Sandbox Code Playgroud)\n\n

这意味着从版本 2.6.26 开始,您可以跟踪init,当然您仍然必须是 root 才能执行此操作。我系统上的二进制文件strace允许我跟踪init,事实上我什至可以用来gdb附加init和杀死它。(当我这样做时,系统立即停止了。)

\n\n

ptrace进程不能使用它来跟踪自身,因此如果strace不检查,它仍然会在跟踪自身时失败。以下程序:

\n\n
#include <sys/ptrace.h>\n#include <stdio.h>\n#include <unistd.h>\nint main() {\n    if (ptrace(PTRACE_ATTACH, getpid(), 0, 0) == -1) {\n        perror(NULL);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

打印Operation not permitted结果是EPERM)。内核执行此检查ptrace.c

\n\n
 retval = -EPERM;\n if (unlikely(task->flags & PF_KTHREAD))\n         goto out;\n if (same_thread_group(task, current)) // <-- this is the one\n         goto out;\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在,两个strace进程可以互相跟踪;内核不会阻止这种情况,你可以自己观察结果。对我来说,第一个strace进程(PID = 5882)打印的最后一件事是:

\n\n
ptrace(PTRACE_SEIZE, 5882, 0, 0x11\n
Run Code Online (Sandbox Code Playgroud)\n\n

而第二个strace进程(PID = 5890)根本不打印任何内容。ps显示两个进程都处于 state t,根据proc(5)手册页,这意味着跟踪已停止。

\n\n

发生这种情况的原因是,只要被跟踪者进入或退出系统调用以及即将向其传递信号(除了SIGKILL),它就会停止。

\n\n

假设进程 5882 已经在跟踪进程 5890。那么,我们可以推断出以下事件序列:

\n\n
    \n
  1. 进程5890进入ptrace系统调用,尝试跟踪进程5882。进程5890进入跟踪停止。
  2. \n
  3. 进程 5882 收到SIGCHLD通知,其被跟踪者进程 5890 已停止。(跟踪停止的进程看起来好像收到了“SIGTRAP”信号。)
  4. \n
  5. 进程 5882 看到它的被跟踪者已经进行了系统调用,就尽职尽责地打印出有关进程 5890 将要进行的系统调用的信息以及参数。这是您看到的最后一个输出。
  6. \n
  7. 进程 5882 调用ptrace(PTRACE_SYSCALL, 5890, ...)以允许进程 5890 继续。
  8. \n
  9. 进程 5890 离开跟踪停止并执行其ptrace(PTRACE_SEIZE, 5882, ...). 当后者返回时,进程5890进入跟踪停止。
  10. \n
  11. 进程 5882 被发送SIGCHLD,因为它的被跟踪者刚刚再次停止。由于正在跟踪,因此接收到信号会使其进入跟踪停止状态。
  12. \n
\n\n

现在两个进程都停止了。结束。

\n\n

从这个例子中可以看出,两个进程相互跟踪的情况不会给内核带来任何固有的逻辑困难,这可能就是为什么内核代码不包含检查来防止这种情况发生的原因。只是对于两个进程相互跟踪来说并不是很有用。

\n