RFC-0244:提高使用者定義的 Zircon 例外狀況

RFC-0244:提交使用者定義的 Zircon 例外狀況
狀態已接受
區域
  • 核心
說明

引入系統呼叫,用於提出使用者定義的 Zircon 例外狀況

Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2024-03-22
審查日期 (年-月-日)2024-04-22

摘要

本 RFC 會介紹 zx_thread_raise_exception 系統呼叫,可觸發使用者定義的 Zircon 例外狀況。這個系統呼叫的第一個用途是,當其中一個程序呼叫 exec() 時,Starnix 會向偵錯工具發出信號。偵錯工具會使用這項信號,判斷開發人員是否要附加至程序。

提振精神

在 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 程序,這就是端對端流程。

需求條件

  1. 當 Starnix 程序呼叫 exec() 時,必須通知偵錯工具,以便檢查程序是否符合任何篩選器 (例如程序的新名稱是否符合名稱篩選器)。
  2. 如果沒有執行偵錯工具,通知機制不應耗用大量資源。
  3. 通知機制必須處理多個偵錯代理程式同時執行的情況。
  4. 設計不應要求我們變更系統的其他部分 (例如crashsvc) 的其他參與者。

設計

偵錯工具會透過在 ZX_EXCEPTION_CHANNEL_TYPE_JOB_DEBUGGER 上監聽 ZX_EXCP_PROCESS_STARTING 例外狀況,瞭解系統正在建立的新程序。本 RFC 中的做法是透過 ZX_EXCEPTION_CHANNEL_TYPE_JOB_DEBUGGER 傳送其他類型的例外狀況,通知偵錯工具程序名稱變更。

很抱歉,我們不希望 Zircon 在程序的 ZX_PROP_NAME 屬性變更時自動產生例外狀況,因為該屬性可由任意執行緒變更。我們希望從名稱變更程序的執行緒產生例外狀況。幸運的是,Starnix 一律會透過 exec()procfs 中的檔案,從程序中的執行緒變更程序名稱,而該檔案只能在程序中寫入。

因此,我們推出了新的系統呼叫,用於產生使用者定義的例外狀況。只要 Starnix 變更程序名稱,就會使用這個系統呼叫來提出這類例外狀況。偵錯工具會監聽這些例外狀況,並重新掃描其附加篩選器清單,看看使用者是否想以新名稱偵錯程序。

使用者定義的例外狀況

與 Zircon 物件上的使用者定義信號類似,此 RFC 會為使用者定義的例外狀況保留部分 Zircon 例外狀況命名空間。這項保留動作可確保使用者定義的例外狀況不會與系統定義的例外狀況日後的擴充功能相衝突。

具體來說,本 RFC 會定義新的 zx_excp_type_t,其中包含名為 ZX_EXCP_USERZX_EXCP_SYNTH 位元集:

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

這個 RFC 也定義了幾個常見的使用者例外狀況代碼,這些代碼會顯示在 zx_exception_context_tsynth_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)

ZX_EXCP_USER_CODE_PROCESS_NAME_CHANGED 程式碼將由 Starnix 和偵錯工具用於上述用途。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 欄位。contextsynth_codesynth_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 上使用 brkint3 指令,這表示新增核心中介機制產生例外狀況的風險也隨之降低。

隱私權注意事項

雖然程序名稱可能含有隱私權敏感資訊,但這項新機制不會向任何新程序提供該資訊的存取權。舉例來說,與同時攜帶新程序名稱和例外狀況的設計相比,這個設計的隱私權屬性稍微較佳。

測試

新的系統呼叫會透過 Zircon 核心測試。

整合測試會測試偵錯工具整合。

說明文件

新的系統呼叫會透過系統呼叫手冊頁面記錄,這與 Zircon 系統呼叫的一般情況相同。新的例外狀況語意也會記錄在「例外狀況處理概念」頁面。

缺點、替代方案和未知事項

我們考慮了許多替代方案:

使用微型架構例外狀況

我們可以使用現有的微架構機制來產生例外狀況,而非新增系統呼叫來產生例外狀況 (例如 brkint3 指令)。這種方法的缺點是,除非處理這些例外狀況,否則會導致嚴重錯誤。我們可以教導 crashsvc 辨識這些例外狀況,就像我們教導回溯要求一樣,但我們不想教導 crashsvccrashsvc 無關的系統功能。

在程序名稱變更時自動產生例外

我們可以讓 Zircon 在程序名稱變更時自動產生例外狀況,而非要求 Starnix 在變更程序名稱後呼叫 zx_thread_raise_exception。不過,如上所述,Zircon 程序的名稱可由任何具有程序句柄的執行緒變更,而 Zircon 缺乏在遠端執行緒上產生例外的機制。

幸運的是,Starnix 實際變更程序名稱的情況,只有在該程序中的執行緒發生名稱變更時才會發生,也就是說,我們不需要解決在遠端執行緒上產生例外狀況的問題,就能解決手邊的用途。