Pre*_*rem 5 kernel monitoring recursive strace
我有一个假设的情况:
假设我们有两个 strace 进程 S1 和 S2,它们只是相互监视。
这怎么可能?
好吧,在 strace 的命令行选项中,-p PID是传递所需 PID 的方式,当我们发出 strace 命令时,它(在我们的例子中)还不知道。我们可以更改 strace 源代码,这-P 0意味着向用户询问 PID。例如,来自 STDIN 的 read()。当我们可以在两个 shell 会话中运行两个 strace 进程并在第三个 shell 中找到 PID 时,我们可以将该输入提供给 S1 和 S2 并让它们相互监视。
S1 和 S2 会卡住吗?或者,进入无限循环,或立即崩溃或...?
再一次,假设我们有另一个 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)
:-)
我只回答Linux。
\n\n令人惊讶的是,在较新的内核中,为了实际执行跟踪而ptrace使用的系统调用被允许跟踪 init 进程。strace手册页说:
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).\nRun Code Online (Sandbox Code Playgroud)\n\n这意味着从版本 2.6.26 开始,您可以跟踪init,当然您仍然必须是 root 才能执行此操作。我系统上的二进制文件strace允许我跟踪init,事实上我什至可以用来gdb附加init和杀死它。(当我这样做时,系统立即停止了。)
ptrace进程不能使用它来跟踪自身,因此如果strace不检查,它仍然会在跟踪自身时失败。以下程序:
#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}\nRun Code Online (Sandbox Code Playgroud)\n\n打印Operation not permitted(即结果是EPERM)。内核执行此检查ptrace.c:
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;\nRun Code Online (Sandbox Code Playgroud)\n\n现在,两个strace进程可以互相跟踪;内核不会阻止这种情况,你可以自己观察结果。对我来说,第一个strace进程(PID = 5882)打印的最后一件事是:
ptrace(PTRACE_SEIZE, 5882, 0, 0x11\nRun Code Online (Sandbox Code Playgroud)\n\n而第二个strace进程(PID = 5890)根本不打印任何内容。ps显示两个进程都处于 state t,根据proc(5)手册页,这意味着跟踪已停止。
发生这种情况的原因是,只要被跟踪者进入或退出系统调用以及即将向其传递信号(除了SIGKILL),它就会停止。
假设进程 5882 已经在跟踪进程 5890。那么,我们可以推断出以下事件序列:
\n\nptrace系统调用,尝试跟踪进程5882。进程5890进入跟踪停止。SIGCHLD通知,其被跟踪者进程 5890 已停止。(跟踪停止的进程看起来好像收到了“SIGTRAP”信号。)ptrace(PTRACE_SYSCALL, 5890, ...)以允许进程 5890 继续。ptrace(PTRACE_SEIZE, 5882, ...). 当后者返回时,进程5890进入跟踪停止。SIGCHLD,因为它的被跟踪者刚刚再次停止。由于正在跟踪,因此接收到信号会使其进入跟踪停止状态。现在两个进程都停止了。结束。
\n\n从这个例子中可以看出,两个进程相互跟踪的情况不会给内核带来任何固有的逻辑困难,这可能就是为什么内核代码不包含检查来防止这种情况发生的原因。只是对于两个进程相互跟踪来说并不是很有用。
\n