RFC-0244:引发用户定义的 Zircon 异常

RFC-0244:引发用户定义的 Zircon 异常
状态已接受
领域
  • 内核
说明

引入用于引发用户定义的 Zircon 异常的系统调用

Gerrit 更改
作者
审核人
提交日期(年-月-日)2024-03-22
审核日期(年-月-日)2024-04-22

摘要

此 RFC 引入了 zx_thread_raise_exception 系统调用,这会引发 用户定义的 Zircon 异常。此系统调用的第一个用例是 Starnix 会在其某个进程调用 exec() 时向调试程序发出信号。 调试程序将使用此信号来确定开发者是否要 附加到进程

设计初衷

在 Starnix 中运行进程时,我们经常希望使用进程名称, 指定是否要将调试程序连接到该进程。这个 此方法非常适用于进程已在运行的情况,因为调试程序可以 检查现有进程的 ZX_PROP_NAME,找到我们所需的进程。 不过,这种方法不太适用于尚未 因为 Starnix 进程是由 fork() 创建的,此时其 ZX_PROP_NAME 与调用 fork() 的进程名称匹配。Starnix 在 exec() 期间更改进程的 ZX_PROP_NAME,但调试程序 不会注意到,因此不会附加到流程中。

利益相关方

谁对是否接受此 RFC 有影响?(此部分为可选内容, encouraged.)

教员

由 FEC 指定通过 RFC 管理此 RFC 的人员 过程。

审核者

  • cpu@google.com
  • jamesr@google.com
  • jruthe@google.com

已咨询

列出应审查 RFC 但无需批准的人员。

社交化

我在 Zircon 聊天频道中讨论过这个问题, 我从那里收到的建议创建原型 按照名称附加到尚未运行的 Starnix 进程的流程, 用户定义的异常。

要求

  1. 当 Starnix 进程调用 exec() 时,必须通知调试程序, 它是否可以检查进程是否与任何过滤器匹配(例如, 进程的新名称是否与名称过滤器匹配)。
  2. 如果没有任何通知, 调试程序正在运行。
  3. 通知机制必须处理多个调试代理的情况 同时运行
  4. 设计不应要求我们更改系统的其他部分(例如 crashsvc)。

设计

调试程序通过监听 发生 ZX_EXCP_PROCESS_STARTING 异常情况时, ZX_EXCEPTION_CHANNEL_TYPE_JOB_DEBUGGER。此 RFC 中的方法是 通过向调试程序发送其他类型的异常,使进程名称发生更改的 一个 ZX_EXCEPTION_CHANNEL_TYPE_JOB_DEBUGGER

很遗憾,我们不希望 Zircon 自动生成异常 当进程的 ZX_PROP_NAME 属性发生更改时触发,因为该属性可以 由任意线程更改相反,我们希望例外情况是 (由具有名称更改的进程的线程生成)。幸运的是 Starnix 始终会更改进程内的进程名称, 通过 exec()procfs 中只能从内部写入的文件 一个进程。

因此,我们引入了一个新的系统调用,用于生成用户定义的 异常。每当 Starnix 更改进程名称时,Starnix 会使用 来引发此类异常。调试程序将监听这些 然后重新扫描其附加过滤器列表,看看用户是否希望 调试进程。

用户定义的异常

与 Zircon 对象上用户定义的信号类似,此 RFC 保留了一部分 用户定义的异常的 Zircon 异常命名空间。这项预订 可确保用户定义的异常不会与未来扩展的 系统定义的异常

具体来说,此 RFC 使用 ZX_EXCP_SYNTH 定义了一个新的 zx_excp_type_t 名为 ZX_EXCP_USER 的位集:

#define ZX_EXCP_USER                    ((uint32_t) 0x309u | ZX_EXCP_SYNTH)

此 RFC 还定义了一些众所周知的用户异常代码,这些代码显示在 zx_exception_context_t 中的 synth_code 字段。

ZX_EXCP_USER_CODE_PROCESS_NAME_CHANGED  ((uint32_t) 0x0001u)
ZX_EXCP_USER_CODE_USER0                 ((uint32_t) 0xF000u)
ZX_EXCP_USER_CODE_USER1                 ((uint32_t) 0xF001u)
ZX_EXCP_USER_CODE_USER2                 ((uint32_t) 0xF002u)

Starnix 将使用 ZX_EXCP_USER_CODE_PROCESS_NAME_CHANGED 代码, 上述用例的调试程序。ZX_EXCP_USER_CODE_USER0ZX_EXCP_USER_CODE_USER1ZX_EXCP_USER_CODE_USER2 代码适用于 特定于应用的用途,类似于 PA_USER0PA_USER1PA_USER2

少于 ZX_EXCP_USER_CODE_USER0 的代码将预留给系统范围使用, 并可在以后的 RFC 中定义。

引发用户定义的异常

此 RFC 定义了用于引发用户定义的异常的系统调用:

zx_status_t zx_thread_raise_exception(uint32_t options,
                                      zx_excp_type_t type,
                                      const zx_exception_context_t* context);

此系统调用会在当前线程上引发 type 类型的异常,且 给定的异常上下文。

目前,options 参数必须为 ZX_EXCEPTION_JOB_DEBUGGER, 的值为 1如果调用方传递任何其他值,系统调用 返回 ZX_ERR_INVALID_ARGS。如果提供此值,异常将 传递给作业调试程序通道(如果存在)。

type 参数必须为 ZX_EXCP_USER。如果调用方传递了 值,系统调用会返回 ZX_ERR_INVALID_ARGS

系统会忽略 contextarch 字段。synth_codesynth_data context 中的字段目前是用来 通过异常获取相关信息

如果我们希望扩展此系统调用,以便为其他类型的 异常通道,我们可以在后面的 RFC 中扩展系统调用的语义。

实现

此功能将通过添加设计中描述的系统调用来实现 部分。用于提出例外情况的所有机制都存在于 锆石。第二个 CL 将指示 debug_agent 监听这些异常 并重新检查生成异常的进程的名称。

概念验证 CL 证明,在 Zircon 和 debug_agent 非常简单。

性能

此设计对系统性能的影响微乎其微。在常见的情况是 没有正在运行的 debug_agentzx_thread_raise_exception 将 在走动到任务层次结构的根部时很早就返回,并注意到 没有人在监听调试程序异常通道。

相反,如果系统中正在运行许多 debug_agent 实例, 此机制会高效地向每一方发送此通知。

由于相同的 机制用于通知 debug_agent 其他常见事件,例如 启动进程

工效学设计

此 RFC 中描述的方法并非特别符合人体工程学。例如: Starnix 需要记住,每当 更改进程名称更符合人体工程学的设计是让 Zircon 能够 每当进程名称发生更改时自动引发此异常。不过, 这种方法比较难,因为进程名称可以从任意一个节点 这是系统中具有进程句柄的线程Zircon 没有 从远程线程引发异常的机制,并添加此类 会大大增加 Zircon 的复杂性(例如, 在返回用户空间之前有待处理的异常)。

向后兼容性

本文档中介绍的设计向后兼容现有的 系统。zx_thread_raise_exception 可能生成的异常包括 明确标记为由用户生成的异常,并具有与 内核生成的异常。该设计还预留了命名空间 用户和内核生成的异常

安全注意事项

zx_thread_raise_exception 为用户空间提供了一种生成 之前无法生成的异常,而攻击者可能会 操控正在监听异常的软件。此提案 通过限制用户生成的例外, ZX_EXCP_SYNTH 位设置并设为包含这些异常的预留命名空间。

用户空间已经在微架构级别生成一些异常, 例如在 ARM 和 Intel 上使用 brkint3 指令, 分别表示为 Cloud Storage 存储分区添加内核中介机制的风险, 生成异常的概率也有所减少。

隐私注意事项

虽然进程名称可能包含隐私敏感信息, 这一新机制并不会使任何新的应用 过程。例如,相较于 包含新进程名称和例外的设计。

测试

新的系统调用将由 Zircon 核心测试进行测试。

系统将通过集成测试来测试调试程序集成。

文档

与往常一样,新的系统调用将记录在系统调用手册页 Zircon 系统调用。新的异常语义也会记录在 异常处理概念页面。

缺点、替代方案和未知问题

我们考虑了多种替代方案:

使用微架构例外情况

我们无需添加系统调用来引发异常,而是可以使用现有的 用于引发异常的微架构机制(例如 brkint3) 说明)。这种方法的缺点是,这些异常会导致 处理。我们可以训练 crashsvc 识别这些异常,因为 适用于回溯请求,但我们不希望向 crashsvc 介绍 与 crashsvc 无关的系统功能。

更改进程名称时自动生成异常

无需 Starnix 在之后调用 zx_thread_raise_exception 更改进程名称时,我们可以让 Zircon 生成异常 自动应用。不过,正如我们前面介绍的 如上面所示,Zircon 进程的名称可以由任何具有 进程句柄,并且 Zircon 缺少生成异常的机制 进行远程线程处理

幸运的是,只有 Starnix 真正更改了名称 即名称更改在其某个线程上发生 因此,我们无需解决生成 异常,以解决当前用例的问题。