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 进程的流程, 用户定义的异常。
要求
- 当 Starnix 进程调用
exec()
时,必须通知调试程序, 它是否可以检查进程是否与任何过滤器匹配(例如, 进程的新名称是否与名称过滤器匹配)。 - 如果没有任何通知, 调试程序正在运行。
- 通知机制必须处理多个调试代理的情况 同时运行
- 设计不应要求我们更改系统的其他部分(例如
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_USER0
、
ZX_EXCP_USER_CODE_USER1
和 ZX_EXCP_USER_CODE_USER2
代码适用于
特定于应用的用途,类似于 PA_USER0
、PA_USER1
和 PA_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
。
系统会忽略 context
的 arch
字段。synth_code
和synth_data
context
中的字段目前是用来
通过异常获取相关信息
如果我们希望扩展此系统调用,以便为其他类型的 异常通道,我们可以在后面的 RFC 中扩展系统调用的语义。
实现
此功能将通过添加设计中描述的系统调用来实现
部分。用于提出例外情况的所有机制都存在于
锆石。第二个 CL 将指示 debug_agent
监听这些异常
并重新检查生成异常的进程的名称。
概念验证 CL 证明,在 Zircon 和
debug_agent
非常简单。
性能
此设计对系统性能的影响微乎其微。在常见的情况是
没有正在运行的 debug_agent
,zx_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 上使用 brk
和 int3
指令,
分别表示为 Cloud Storage 存储分区添加内核中介机制的风险,
生成异常的概率也有所减少。
隐私注意事项
虽然进程名称可能包含隐私敏感信息, 这一新机制并不会使任何新的应用 过程。例如,相较于 包含新进程名称和例外的设计。
测试
新的系统调用将由 Zircon 核心测试进行测试。
系统将通过集成测试来测试调试程序集成。
文档
与往常一样,新的系统调用将记录在系统调用手册页 Zircon 系统调用。新的异常语义也会记录在 异常处理概念页面。
缺点、替代方案和未知问题
我们考虑了多种替代方案:
使用微架构例外情况
我们无需添加系统调用来引发异常,而是可以使用现有的
用于引发异常的微架构机制(例如 brk
和 int3
)
说明)。这种方法的缺点是,这些异常会导致
处理。我们可以训练 crashsvc
识别这些异常,因为
适用于回溯请求,但我们不希望向 crashsvc
介绍
与 crashsvc
无关的系统功能。
更改进程名称时自动生成异常
无需 Starnix 在之后调用 zx_thread_raise_exception
更改进程名称时,我们可以让 Zircon 生成异常
自动应用。不过,正如我们前面介绍的
如上面所示,Zircon 进程的名称可以由任何具有
进程句柄,并且 Zircon 缺少生成异常的机制
进行远程线程处理
幸运的是,只有 Starnix 真正更改了名称 即名称更改在其某个线程上发生 因此,我们无需解决生成 异常,以解决当前用例的问题。