【发布时间】:2019-09-25 05:29:08
【问题描述】:
我正在创建一个syscall tracer using seccomp。我没有更改系统调用中的任何内容,我只是将其记录在我的结构中,当进程完成时 - 我将这个结构转储到磁盘上。
当我像这样运行我的程序时(它被称为 tracer):
跟踪环境
一切正常,之后我在文件中看到了日志。
但是,如果我尝试跟踪内部调用 execve 的程序,它会失败:
tracer watch -n1 env
或
tracer strace -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),
};
我没有列出整个代码,因为它很明显并且只能以单一方式编写,而且它是在我上面提到的文章中编写的。这个问题也被称为in the Internet,但我找不到任何解决方案。如果您仍然坚持使用整个代码(我对此表示怀疑)或 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
我花在这个问题上的时间越多,我就越意识到问题实际上出在内核的源代码中。 It copies the filters from one process to another,孩子一,但他们不复制实现,所以所有 SECCOMP_RET_TRACE 规则都被复制并且孩子中没有跟踪器,所以子孩子中的每个系统调用都返回 -ENOSYS 因为没有tracer 那里,但是,规则被复制。
【问题讨论】:
-
可能是 message traffic from
execveneeds to be handled 导致问题的方式。 -
@ryyker 谢谢帮忙,但是不知道这两个问题是怎么联系的?请你澄清一下好吗?
-
他们可能不是。这是在黑暗中拍摄的。鉴于您的环境和条件不太可能轻易重现,剩下的就是试图从用于描述问题的词语中推断出因果关系,例如:however ... with execve,它在您帖子的第一部分失败。我看到您编辑了似乎是关于问题性质的伪结论?