RFC-0178:每个作业有多个调试异常通道

RFC-0178:每个作业有多个调试异常通道
状态已接受
领域
  • 内核
说明

Zircon 允许在一项作业中使用多个调试异常通道。

问题
  • 97848
Gerrit 更改
  • 688614
作者
审核人
提交日期(年-月-日)2022-06-08
审核日期(年-月-日)2022-07-12

总结

此 RFC 提议更改 zx_task_create_exception_channel,以允许在一个作业上创建最多 32 个调试异常通道。

设计初衷

Fuchsia 上的调试程序依赖于在作业上创建调试异常通道来监控进程启动,以便捕获相关进程并快速附加到该进程。调试异常通道通常在系统的根作业上创建,以便调试程序可以监控所有进程。

进程启动时,内核会遍历作业层次结构,以查找要通知的调试作业异常渠道。该步行从包含新进程的作业开始,一直向上进入层次结构,直到找到调试作业异常渠道或到达根作业。

不过,目前的实现方法有两个缺点。

  • 对于给定作业,最多只能有一个调试异常渠道,这意味着,最多只能有一个调试程序可以监控作业层次结构的任何给定子树。
  • 系统仅通知第一个找到的调试异常通道,即子作业上的调试程序阻止父作业上的调试程序观察进程启动。

利益相关方

教员:cpu@google.com

审核者:brettw@google.com、maniscalco@google.com

咨询人员:johngro@google.com

社交:这一想法在 Fuchsia 的内核进化工作组讨论了。

设计

我们提议 Zircon 允许在一个作业中使用多个调试作业异常通道。

Zircon 中有 5 种异常渠道:调试作业、作业、调试进程、进程和线程。每个字段最多只能在相应对象上创建一次。设置此限制是为了避免棘手的情况,例如多个异常处理程序为 ZX_PROP_EXCEPTION_STATE 设置不同的值。

不过,“调试作业”在这里的区别在于,它是仅用于通知的渠道:它能收到的唯一异常类型是 ZX_EXCP_PROCESS_STARTING,其中 ZX_PROP_EXCEPTION_STATE 会被忽略。因此,您可以在一个作业中允许多个调试异常渠道,而不必担心不一致。

在一个作业上创建多个调试作业异常渠道后,ZX_EXCP_PROCESS_STARTING 事件将按顺序发送到所有渠道,以便多个监听器检查进程。只有一个调试程序可以附加到给定进程的限制不会改变,因为“调试进程”异常通道仍是专有的,并且除第一个调试程序之外的所有调试程序都将获得 ZX_ERR_ALREADY_BOUND

此外,我们还提议修改 ZX_EXCP_PROCESS_STARTING 事件的事件传播,以便即使子作业上创建了异常渠道,一个事件也会一直传播到根。作业层次结构中较低级别的调试作业异常渠道会先于较高级别的调试作业异常渠道收到通知。在任何给定级别,先前创建的渠道都将先于后期创建的渠道收到通知。仅当所有异常渠道都收到通知且异常对象关闭时,该进程才能继续。

请注意,任何异常消息接收器只要不关闭异常对象的句柄,就有可能无限期地阻止进程的启动。但是,它们无法完全停止进程启动。

实现

系统调用不会发生 API 或 ABI 更改。zx_task_create_exception_channel 的现有行为将更改,以允许创建最多 N 个频道,而不是在第一个频道之后返回 ZX_ERR_ALREADY_BOUND

性能

性能可能会下降,因为有更多监听器可能会阻止进程启动。不过,调试异常渠道不应在生产环境中使用,也不会由长时间运行的程序占用。它仅适用于调试程序或类似调试工具,因此影响应该最小。

一般来说,调试程序应在执行必要的设置后立即关闭异常句柄。

安全注意事项

为避免针对内核的 DoS 攻击,我们应限制在一个作业中创建的调试作业异常通道的数量上限。此限制应足够大,以便运行任意数量的调试程序,例如 32 个。

测试

将向 //zircon/system/utest/debugger 添加新的测试用例以涵盖此功能。

文档

我们将更新描述异常处理zx_task_create_exception_channel 的文档,以反映这一变更。

缺点、替代方案和未知情况

替代方案:用户空间的进程委托在根作业上启动

此方法可避免对内核进行更改。相反,用户空间委托将保留根作业调试通道,并提供 FIDL 接口供调试程序用于订阅进程启动。委托可以是立即提供 fuchsia.kernel.RootJob 的组件管理器本身,也可以是独立程序。

协议类似于

@discoverable
protocol RootJob {
    /// Hanging get pattern
    GetProcessStartingEvent() -> (resource struct {
        process zx.handle:PROCESS;
        continue zx.handle:EVENTPAIR;  // dropping this eventpair will propagate
                                       // the event to the next listener.
    }) error zx.status;
};

这种方法的问题

  • 它仅适用于根作业。调试程序需要两个逻辑来用于根作业和非根作业。
  • 在子作业上创建的异常渠道可以阻止事件传播到根。
  • 引入具有类似系统调用提供的功能的 FIDL API 会令人困惑。

替代方案:debug_agent 支持多个客户端

我们还可以使 debug_agent 支持多个客户端来解决当前问题,即 debug_agent 变为单例。

问题在于

  • 我们会做更多工作。
  • debug_agent 将成为 Fuchsia 的指定调试程序。其他调试程序必须与 debug_agent 通信,这将显示一个大型且不稳定的接口。