Can';t跟踪子进程';s syscalls,它使用ptrace和seccomp调用execve
我正在创造一个新的世界。我不改变系统调用中的任何内容,我只是将它记录在我的结构中,当进程完成时,我将这个结构转储到磁盘上 当我像这样运行我的程序时(它被称为跟踪器): 示踪环境 一切正常,我在文件中看到了日志。 但是,如果我试图跟踪一个在内部调用Can';t跟踪子进程';s syscalls,它使用ptrace和seccomp调用execve,c,linux,C,Linux,我正在创造一个新的世界。我不改变系统调用中的任何内容,我只是将它记录在我的结构中,当进程完成时,我将这个结构转储到磁盘上 当我像这样运行我的程序时(它被称为跟踪器): 示踪环境 一切正常,我在文件中看到了日志。 但是,如果我试图跟踪一个在内部调用execve的程序,它会失败: 追踪观察-n1环境 或 示踪剂带-o/tmp/log env 使用标准输出失败 环境:加载共享库时出错:无法为搜索路径创建缓存:无法分配内存 和日志: $ cat /tmp/log execve("/usr/bin/env
execve
的程序,它会失败:
追踪观察-n1环境
或
示踪剂带-o/tmp/log env
使用标准输出失败
环境:加载共享库时出错:无法为搜索路径创建缓存:无法分配内存
和日志:
$ cat /tmp/log
execve("/usr/bin/env", ["env"], [/* 19 vars */]) = 0
brk(NULL) = 0x415000
mmap(0xffffffffffffffda, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2
writev(103, [{iov_base="env", iov_len=3}, {iov_base=": ", iov_len=2}, {iov_base="error while loading shared libraries", iov_len=36}, {iov_base=": ", iov_len=2}, {iov_base="", iov_len=0}, {iov_base="", iov_len=0}, {iov_base="cannot create cache for search path", iov_len=35}, {iov_base=": ", iov_len=2}, {iov_base="Cannot allocate memory", iov_len=22}, {iov_base="\n", iov_len=1}], 10) = 127
+++ exited with 127 +++
请注意奇怪的mmap
地址及其返回值。我不明白是什么错了,为什么会这样。任何其他程序都可以正常工作,因此我想问题在于将seccomp
过滤器复制到调用execve
的分叉进程
以下是我的seccomp
规则:
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct seccomp_data, nr)),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_openat, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_write, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_mmap, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_mprotect, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_close, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
};
我没有列出全部代码,因为这是显而易见的,并且只能以一种方式编写,而且它是在我上面提到的文章中编写的。这个问题也是众所周知的,但我没有找到任何解决办法。如果您仍然坚持完整的代码(我对此表示怀疑)或MCVE,我可以提供它
另外,当我添加execve
跟踪时,我有不同的行为:
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct seccomp_data, nr)),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_openat, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_write, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_mmap, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_mprotect, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_close, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_execve, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
};
日志变为:
$ cat /tmp/log
execve(0xffffffffffffffda, ["env"], [/* 19 vars */]) = -1 ENOSYS (Function not implemented)
getpid() = 15535
exit_group(1) = ?
+++ exited with 1 +++
Linux 4.4 aarch64、Linux 4.15 x86-64
我花在这个问题上的时间越多,我就越意识到这个问题实际上存在于内核的源代码中,但是他们不复制实现,所以所有的
SECCOMP\u RET\u TRACE
规则都被复制,并且子孩子中没有跟踪器,所以子孩子中的每个系统调用都返回-ENOSYS
,因为那里没有跟踪器,但是,规则被复制了。我找到了解决这个问题的方法。要为子进程设置跟踪程序,或者至少避免子进程的ENOSYS
问题,我们可以在设置PTRACE选项时指定PTRACE\u O\u TRACEFORK
和PTRACE\u O\u TRACECLONE
标志,如下所示:
ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESECCOMP | PTRACE_O_TRACEFORK | PTRACE_O_TRACECLONE);
我们需要同时添加这两个选项的原因很难简单解释。首先,系统中存在哪些系统调用以及程序使用哪些系统调用取决于体系结构和libc(通常通过libc实现)。也许,即使这个列表也不完整:我们可能还必须跟踪vWork
以及与克隆(或繁殖)线程或进程相关的其他方式(记住,线程在Linux中是轻量级进程)。因此,这些选项的作用在以下文件中指定:
PTRACE\u O\u TRACECLONE
(从Linux 2.5.46开始)
在下一个克隆(2)处停止tracee并自动
开始跟踪新克隆的进程,该进程将
从SIGSTOP
开始,或PTRACE\u事件\u STOP
开始,如果
使用了PTRACE\u-take
。跟踪器的等待PID(2)将
返回一个状态值,以便
status>>8 == (SIGTRAP | (PTRACE_EVENT_CLONE<<8))
这可能是导致问题的原因。@Ryker感谢您的帮助,但我不确定这两个问题是如何联系在一起的?请你澄清一下好吗?可能不是。这是在黑暗中拍摄的。鉴于你的环境和条件不太可能轻易重现,剩下的就是试图从描述问题的词语中推断出因果关系,例如:然而。。。对于Exeve,它在文章的第一部分失败。我看到你编辑了一篇关于问题本质的伪结论?
const pid_t childWaited = waitpid(-1, &status, 0);
// but not const pid_t result = waitpid(myChildPid, &status, 0);