系統呼叫使用者分發

背景

諸如 Wine 之類的相容層需要一種有效的方式來模擬程序中只有一部分的系統呼叫 - 具有不相容程式碼的部分 - 同時能夠以很高的效能執行本機系統呼叫,而不會對程序的本機部分造成效能損失。 Seccomp 在此任務上有所不足,因為它對基於記憶體區域有效過濾系統呼叫的支援有限,並且不支援刪除過濾器。 因此,需要一種新的機制。

系統呼叫使用者分發將系統呼叫分發器地址的過濾帶回使用者空間。應用程式可以控制一個翻轉開關,指示程序的當前個性。然後,多重個性應用程式可以在跨越相容層 API 邊界時翻轉開關,而無需呼叫核心,以啟用/停用系統呼叫重定向並透過 SIGSYS 直接執行系統呼叫(停用)或將它們傳送到使用者空間進行模擬。

此設計的目標是提供非常快速的相容層邊界交叉,這可以透過每次相容層執行時都不執行系統呼叫來更改個性來實現。相反,暴露給核心的使用者空間記憶體區域指示當前的個性,並且應用程式只需修改該變數即可配置該機制。

在大多數架構(如 x86)上,處理訊號的成本相對較高,但至少對於 Wine 而言,由本機 Windows 程式碼發出的系統呼叫目前尚不存在效能問題,因為它們非常罕見,至少對於現代遊戲應用程式而言。

由於此機制旨在捕獲由非本機應用程式發出的系統呼叫,因此它必須在呼叫 ABI 對於 Linux 完全意外的系統呼叫上起作用。因此,系統呼叫使用者分發不依賴於任何系統呼叫 ABI 來進行過濾。它僅使用系統呼叫分發器地址和使用者空間金鑰。

由於這些攔截的系統呼叫的 ABI 對 Linux 來說是未知的,因此這些系統呼叫無法透過 ptrace 或系統呼叫跟蹤點進行檢測。

介面

執行緒可以透過執行以下 prctl 在受支援的核心上設定此機制

prctl(PR_SET_SYSCALL_USER_DISPATCH, <op>, <offset>, <length>, [selector])

<op> 是 PR_SYS_DISPATCH_ON 或 PR_SYS_DISPATCH_OFF,用於全域性啟用和停用該執行緒的機制。使用 PR_SYS_DISPATCH_OFF 時,其他欄位必須為零。

[<offset>, <offset>+<length>) 劃定一個記憶體區域間隔,無論使用者空間選擇器如何,系統呼叫總是從該記憶體區域直接執行。這為 C 庫提供了一條快速通道,其中包括本機程式碼應用程式中最常見的系統呼叫分發器,並且還提供了一種方法,使訊號處理程式可以返回而不會在 (rt_)sigreturn 上觸發巢狀的 SIGSYS。此介面的使用者應確保至少訊號 trampoline 程式碼包含在此區域中。此外,對於在 vDSO 上實現 trampoline 程式碼的系統呼叫,該 trampoline 永遠不會被攔截。

[selector] 是指向程序記憶體區域中 char 大小的區域的指標,它提供了一種快速啟用/停用執行緒範圍系統呼叫重定向的方法,而無需直接呼叫核心。 selector 可以設定為 SYSCALL_DISPATCH_FILTER_ALLOW 或 SYSCALL_DISPATCH_FILTER_BLOCK。任何其他值都應使用 SIGSYS 終止程式。

此外,可以透過 PTRACE_(GET|SET)_SYSCALL_USER_DISPATCH_CONFIG ptrace 請求來窺視和設定任務的系統呼叫使用者分發配置。這對於檢查點/重啟軟體非常有用。

安全注意事項

系統呼叫使用者分發為相容層提供了快速捕獲由應用程式的非本機部分發出的系統呼叫的功能,同時不影響程序的 Linux 本機區域。它不是沙盒系統呼叫的機制,也不應將其視為安全機制,因為惡意應用程式可以透過跳轉到允許的分發器區域,然後在執行系統呼叫之前,或者發現地址並修改選擇器值來輕鬆破壞該機制。如果用例需要任何型別的安全沙盒,則應使用 Seccomp。

現有程序的任何 fork 或 exec 都會將機制重置為 PR_SYS_DISPATCH_OFF。