在 Linux 系统调用中,系统调用完成后(在 sys_exit 跟踪点)系统调用参数是否保存在寄存器中?

时间:2021-01-23 12:23:41

标签: linux-kernel system-calls tracepoint

是否保证能够读取 sys_exit 跟踪点处的所有系统调用参数?

sysdig driver 是使用内核静态跟踪点捕获系统调用的内核模块。在这个项目中,一些系统调用参数在 sys_enter 跟踪点读取,其他一些参数在 sys_exit 读取(当然是返回值,还有用户空间中的内容以避免页面错误)。

为什么不读取 sys_exit 处的所有参数?这是因为某些参数可能在 sys_exit 处不可用吗?

1 个答案:

答案 0 :(得分:1)

<块引用>

是否保证能够读取 sys_exit 跟踪点处的所有系统调用参数?

是的……也不是,我们需要区分参数和寄存器。 Linux 系统调用应该保留所有通用用户空间寄存器,除了用于返回值的寄存器(在某些架构上还有第二个寄存器来指示是否发生了错误)。然而,这并不意味着系统调用的输入参数不能在进入和退出之间改变:如果一个寄存器保存了指向某些数据的指针的值,而寄存器本身没有改变,则数据它表明很可能会发生变化。

查看静态跟踪点sys_exit的{​​{3}},可以看到仅跟踪系统调用号(id)及其返回值(ret)。有关更多信息,请参阅我的答案底部的注释。

<块引用>

为什么不读取 sys_exit 处的所有参数?这是因为某些参数可能在 sys_exit 处不可用吗?

是的,我会说确保跟踪参数的正确性是仅在出口处跟踪是一个坏主意的主要原因。即使得到了寄存器的值,也无法知道系统调用出口的真实参数。即使系统调用本身保证保存和恢复用户寄存器的状态,系统调用本身也可以改变作为参数传递的数据。例如,recvmsg 系统调用采用指向内存中 struct msghdr 的指针,该指针既用作输入参数又用作输出参数; poll 系统调用使用指向 struct pollfd 的指针执行相同的操作。此外,另一个线程或程序可能在进行系统调用时很好地修改了程序的内存,从而改变了数据。

在特定情况下,系统调用也可能需要很长时间才能返回(例如 sleep,或终端上的阻塞 read,侦听中的 accept插座等)。如果您只在出口处进行跟踪,您将获得非常不正确的时间信息,最重要的是,您必须等待很长时间才能捕获任何有意义的信息,即使该信息在入口点已经可用。


关于 sys_exit 跟踪点的注意事项

虽然您可以从当前任务中提取已保存寄存器的值,但我并不完全确定在 sys_exit 跟踪点中这样做的语义。我搜索了一些关于这个特定案例的文档,但没有运气,内核代码很好......复杂。

到达退出钩子的调用链应该是:

如果在系统调用期间向进程传递了致命信号,而实际进程永远不会到达系统调用的出口(即没有任何值返回给用户空间),则跟踪点仍将被命中。当发生此类信号传递时,会使用特殊的内部返回值,例如 -ERESTARTSYS (trace_sys_exit())。这个值不是一个实际的系统调用返回值(它不会返回到用户空间),而是它仅供内核使用。因此,如果进程接收到致命信号,则看起来 sys_exit 跟踪点正在被特殊的 -ERESTARTSYS 命中。例如,在 SIGSTOP + SIGCONT 的情况下不会发生这种情况。不过,对此持保留态度,因为我无法为此找到适当的文档。