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).\n
Run 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}\n
Run 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;\n
Run Code Online (Sandbox Code Playgroud)\n\n现在,两个strace
进程可以互相跟踪;内核不会阻止这种情况,你可以自己观察结果。对我来说,第一个strace
进程(PID = 5882)打印的最后一件事是:
ptrace(PTRACE_SEIZE, 5882, 0, 0x11\n
Run 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