【问题标题】:Can eBPF modify the return value or parameters of a syscall?eBPF 可以修改系统调用的返回值或参数吗?
【发布时间】:2017-08-17 15:30:13
【问题描述】:

为了模拟一些行为,我想在系统调用上附加一个探针,并在传递某些参数时修改返回值。或者,在函数成为进程之前修改函数的参数也足够了。

BPF 可以做到吗?

【问题讨论】:

  • systemtap 可以做到
  • 如果有人来这里有兴趣使用 eBPF 进行网络连接,XDP 允许对数据包进行一些控制。 Forwarding example

标签: linux system-calls bpf ebpf


【解决方案1】:

我相信将 eBPF 附加到 kprobes/kretprobes 可以让您读取函数参数和返回值,但您不能篡改它们。我不是 100% 确定;请求确认的好地方是 IO Visor 项目mailing list 或 IRC 频道(#iovisor at irc.oftc.net)。

作为替代解决方案,我知道您至少可以使用 -e 选项更改带有 strace 的系统调用的返回值。引用the manual page:

-e inject=set[:error=errno|:retval=value][:signal=sig][:when=expr]
       Perform syscall tampering for the specified set of syscalls.

此外,如果您对这方面有任何兴趣,在 Fosdem 2017 上还有 a presentation 和故障注入。以下是幻灯片中的一个示例命令:

strace -P precious.txt -efault=unlink:retval=0 unlink precious.txt

编辑: 正如 Ben 所说,kprobes 和 tracepoints 上的 eBPF 绝对是只读的,用于跟踪和监控用例。我也在 IRC 上得到了确认。

【讨论】:

    【解决方案2】:

    在内核探针 (kprobes) 中,eBPF 虚拟机对系统调用参数和返回值具有只读访问权限。

    然而,eBPF 程序将有它自己的返回码。可以应用 seccomp 配置文件来捕获 BPF(不是 eBPF;感谢@qeole)返回代码并在执行期间中断系统调用。

    允许的运行时修改是:

    • SECCOMP_RET_KILL: 立即用SIGSYS 杀死
    • SECCOMP_RET_TRAP:发送一个可捕获的SIGSYS,提供模拟系统调用的机会
    • SECCOMP_RET_ERRNO:强制errno
    • SECCOMP_RET_TRACE:将产量决策设置为 ptracer 或将 errno 设置为 -ENOSYS
    • SECCOMP_RET_ALLOW:允许

    https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt

    SECCOMP_RET_TRACE 方法允许修改执行的系统调用、参数或返回值。这取决于体系结构,并且对强制外部引用的修改可能会导致 ENOSYS 错误。

    它通过将执行传递给等待的用户空间 ptrace 来做到这一点,它能够修改被跟踪的进程内存、寄存器和文件描述符。

    跟踪器需要调用 ptrace,然后调用 waitpid。一个例子:

    ptrace(PTRACE_SETOPTIONS, tracee_pid, 0, PTRACE_O_TRACESECCOMP);
    waitpid(tracee_pid, &status, 0);
    

    http://man7.org/linux/man-pages/man2/ptrace.2.html

    waitpid 返回时,根据status 的内容,可以使用PTRACE_GETEVENTMSG ptrace 操作检索seccomp 返回值。这将检索 seccomp SECCOMP_RET_DATA 值,这是 BPF 程序设置的 16 位字段。示例:

    ptrace(PTRACE_GETEVENTMSG, tracee_pid, 0, &data);
    

    可以在继续操作之前在内存中修改系统调用参数。您可以使用PTRACE_SYSCALL 步骤执行单个系统调用进入或退出。恢复执行前可以在用户空间修改 Syscall 返回值;底层程序将无法看到系统调用返回值已被修改。

    一个示例实现: Filter and Modify System Calls with seccomp and ptrace

    【讨论】:

    • 小修正:seccomp 仅支持“经典​​”版本的 BPF。目前不支持 eBPF(“扩展 BPF”)。
    • 更新了!谢谢 Qeole。
    【解决方案3】:

    可以使用 eBPF 修改一些用户空间内存。如bpf.h header file中所述:

     * int bpf_probe_write_user(void *dst, const void *src, u32 len)
     *  Description
     *      Attempt in a safe way to write *len* bytes from the buffer
     *      *src* to *dst* in memory. It only works for threads that are in
     *      user context, and *dst* must be a valid user space address.
     *
     *      This helper should not be used to implement any kind of
     *      security mechanism because of TOC-TOU attacks, but rather to
     *      debug, divert, and manipulate execution of semi-cooperative
     *      processes.
     *
     *      Keep in mind that this feature is meant for experiments, and it
     *      has a risk of crashing the system and running programs.
     *      Therefore, when an eBPF program using this helper is attached,
     *      a warning including PID and process name is printed to kernel
     *      logs.
     *  Return
     *      0 on success, or a negative error in case of failure.
    

    另外,引用the BPF design Q&A

    跟踪 BPF 程序可以覆盖当前的用户内存 使用 bpf_probe_write_user() 的任务。每次加载此类程序时 内核会打印警告信息,所以这个助手只有用 用于实验和原型。跟踪 BPF 程序只能是 root。

    您的 eBPF 可能会将数据写入用户空间内存位置。请注意,您仍然无法在您的 eBPF 程序中修改内核结构。

    【讨论】:

      【解决方案4】:

      可以使用 eBPF 将错误注入到系统调用调用中:https://lwn.net/Articles/740146/

      有一个名为bpf_override_return() 的bpf 函数,它可以覆盖调用的返回值。这是一个使用密件抄送作为前端的例子:https://github.com/iovisor/bcc/blob/master/tools/inject.py

      根据Linux manual page

      bpf_override_return() 仅在使用 CONFIG_BPF_KPROBE_OVERRIDE 配置选项编译内核时可用,在这种情况下,它仅适用于内核代码中带有 ALLOW_ERROR_INJECTION 标记的函数。

      此外,帮助程序仅适用于具有CONFIG_FUNCTION_ERROR_INJECTION 选项的架构。在撰写本文时,x86 架构是唯一支持此功能的架构。

      可以向错误注入框架添加功能。更多信息可以在这里找到:https://github.com/iovisor/bcc/issues/2485

      【讨论】:

      • 谢谢!重要的警告,这只适用于内核中少数列入白名单的函数。
      • 谢谢@GeorgSchölly :) 我已经更新了关于白名单的答案。但是我还没有找到错误注入功能的确切默认白名单。它似乎支持内核中的大部分系统调用函数。
      • 看起来只是一些函数,其中大部分与 btrfs 文件系统有关。这些函数在源代码中用ALLOW_ERROR_INJECTION注解。
      猜你喜欢
      • 1970-01-01
      • 2012-02-11
      • 1970-01-01
      • 2013-02-20
      • 2011-04-08
      • 2015-12-01
      • 2021-06-25
      • 2012-10-07
      • 2012-03-10
      相关资源
      最近更新 更多