| 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 是否通過的影響?(這個部分為選填,但建議填寫)。
協助人員:
由 FEC 指派,負責在 RFC 程序中引導此 RFC 的人員。
審查者:
- cpu@google.com
- jamesr@google.com
- jruthe@google.com
已諮詢:
列出應審查 RFC 的人員,但不必取得他們的核准。
社交:
Zircon 即時通訊管道討論了這個問題,我根據收到的建議建立原型,示範如何使用使用者定義的例外狀況,依名稱附加至尚未執行的 Starnix 程序,完成端對端流程。
需求條件
- 當 Starnix 程序呼叫
exec()時,必須通知偵錯工具,以便檢查程序是否符合任何篩選條件 (例如,程序的新名稱是否符合名稱篩選條件)。 - 如果沒有執行偵錯工具,通知機制就不應耗用大量資源。
- 通知機制必須處理多個偵錯代理程式同時執行的情況。
- 設計不應要求我們變更系統的其他部分 (例如
crashsvc) that are not otherwise involved.
設計
偵錯工具會監聽 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_type_t,並將 ZX_EXCP_SYNTH
位元集稱為 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 欄位。目前,透過例外狀況傳達資訊的主要機制是 context 中的 synth_code 和 synth_data 欄位。
如果我們希望將這個系統呼叫擴充至其他類型的例外狀況通道,以便傳送例外狀況,可以在後續的 RFC 中擴充系統呼叫的語意。
實作
如要實作這項功能,請新增設計章節所述的系統呼叫。Zircon 中已存在所有引發例外狀況的機制。第二個 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 指令,這表示新增用於產生例外狀況的 Kernel 介導機制風險也會降低。
隱私權注意事項
雖然程序名稱可能含有隱私權敏感資訊,但這項新機制不會將這類資訊提供給任何新程序。舉例來說,如果設計在例外狀況中攜帶新的程序名稱,隱私權屬性會略遜於這個設計。
測試
Zircon 核心測試會測試新的系統呼叫。
偵錯工具整合功能會透過整合測試進行測試。
說明文件
新系統呼叫會照常記錄在系統呼叫手冊頁面中,新的例外狀況語意也會記錄在「例外狀況處理概念」頁面中。
缺點、替代方案和未知事項
我們考慮了多種替代方案:
使用微架構例外狀況
我們可以使用現有的微架構機制來引發例外狀況 (例如 brk 和 int3 指令),而不必新增系統呼叫來引發例外狀況。這種方法的缺點是,除非處理這些例外狀況,否則會導致嚴重錯誤。我們可以教導 crashsvc 辨識這些例外狀況,就像處理回溯要求一樣,但我們不希望 crashsvc 學習與 crashsvc 無關的系統功能。
在程序名稱變更時自動產生例外狀況
我們不必要求 Starnix 在變更程序名稱後呼叫 zx_thread_raise_exception,而是可以讓 Zircon 在程序名稱變更時自動產生例外狀況。不過,如上所述,任何擁有程序控制代碼的執行緒都可以變更 Zircon 程序的名稱,而 Zircon 缺乏在遠端執行緒上產生例外狀況的機制。
幸好,Starnix 實際變更程序名稱的唯一情況,是該程序內執行緒的名稱發生變更,這表示我們不需要解決在遠端執行緒上產生例外狀況的問題,即可處理目前的使用案例。