RFC-0159:僅執行記憶體

RFC-0159:僅執行記憶體
狀態已接受
領域
  • 核心
  • 工具鏈
說明

支援對應僅執行記憶體。

問題
毛皮變化
作者
審查人員
提交日期 (年-月-日)2022-03-29
審查日期 (年-月-日)2022-05-10

摘要

本文件說明核心 API 的變更,以便支援以下項目的二進位檔: 僅執行中的區隔 (方法是在 zx_system_get_features並變更 launchpadprocess_builder 載入器和 Fuchsia 樹狀結構內 libc 中的動態連結器來支援「--x」 區隔文中列出最終核心支援對應計畫 而且只有在硬體支援時才須執行的網頁。

我們通常不需要在載入後讀取可執行的記憶體。 預設啟用僅供執行的程式碼,可進一步提升 Fuchsia 使用者空間的安全性 ,並進一步實踐工程最佳做法,僅授予最低權限。

提振精神

ARMv7m 中的 ARM MMUs 已支援僅限執行的網頁,並允許網頁 只能對應這些記憶體,因此這些記憶體僅供可執行,無法讀取或 可寫入。儘管可寫入的程式碼頁面已被視為對 並指出維持程式碼的可讀性 進而降低無謂的風險具體來說,閱讀程式碼頁面通常是 防止程式碼被讀取 您的攻擊手段請參閱可讀取的程式碼安全性。此外 支援僅限執行的網頁,很適合採用 Fuchsia 的權限模型, 更符合最低權限原則:程式碼通常不會 但只需要執行

相關人員

講師:

  • cpu@google.com

審查者:

  • phosek@google.com
  • mvanotti@google.com
  • maniscalco@google.com
  • travisg@google.com

背景

僅限執行的記憶體

僅限執行的記憶體 (XOM) 描述了沒有讀取和 且只能執行ARMv7m 以上版本提供原生支援 針對 XOM,但舊版 ISA 有一些需考量的事項。深入討論 XOM 和 PAN

本文件幾乎著重在 AArch64,但實作方式 各架構通用的架構硬體和工具鍊支援成熟的 這些架構都很容易就能使用 都是由 Fuchsia 提供。

程式碼頁面權限

一開始,支援電腦直接存取實體記憶體,而沒有 任何檢查或保護措施MMU 推出的簡介為主要抽象化機制 將程式的記憶體檢視畫面與 基礎實體資源這有助於提高彈性、安全性以及 讓 OS 實作者提供高強度隔離功能,確保安全程式設計模型 透過程序抽象化程序目前的 MMU 提供 像是分頁記憶體、快速位址翻譯 和權限檢查而且可讓使用者 存取及使用記憶體區域 控制是否能讀取、寫入或執行記憶體頁面。這是索引鍵 以便確保程式安全、錯誤隔離和安全防護 程式可藉由強制執行硬體來濫用系統資源 權限檢查

可寫入和可執行的記憶體特別危險,因為 讓對手能夠輕鬆執行任意程式碼 找出常見安全漏洞 (例如緩衝區溢位)因此許多作業系統 設定明確禁止網頁同時可供寫入和執行 (W^X)。這個標準已行之有年,OpenBSD 一直是 2003 年的 W^X,搭配 OpenBSD 3.3 openbsd-wxorx。另請參閱 SELinux W^X 政策 selinux-wxorx。可寫入程式碼對於及時 (JIT) 等功能相當實用 編譯,在執行階段將可執行的指示寫入記憶體。建造 系統可以禁止使用 W|X 網頁,而 JIT 必須解決這個問題。簡單的方法是 將程式碼寫入不可執行的網頁,之後再變更網頁保護措施。 也就是透過 mprotectzx_vmar_protect,以便執行,但無法寫入 example-fuchsia-test.幾乎在所有情況下,W|X 的網頁同樣也是 。同樣地,「可執行的網頁」同樣很少需要讀取 例外狀況。允許讀取可執行網頁的讀取作業為 一般不需要,因此不應使用預設值。

可讀取的程式碼

由於 ARM 的固定指令寬度,即時值具有大小限制 限制。因此,載入作業是使用 PC-Relative 定址完成。若要取得 這邊的虛擬指令 ldr Rd, =imm 會按照原理,發出 imm 靠近載入程式碼的程式碼這與 XOM 不相容,因為 會在必須閱讀的文字部分放置資料搜尋使用 程式碼集內的常值集區,以確保不會讀取可執行的部分, 已發現 Zircon 中的 ldr Rd, =imm 有一些使用記錄,但從此至今 已移除Clang 不會使用常值集區用於 aarch64,而是會發出 建立大量即時物件Clang 具有 -mexecute-only 標記和別名 -mpure-code,但這些欄位在 arm32 上才有意義,因為 指定 aarch64 時,這些旗標是固有的

示例:大型中級

這個範例說明 Clang 如何編譯這個 C 程式碼,以提供不同的 就會指定 clang-example。第一列顯示 aarch64,底部顯示 arm32:

uint32_t a() {
    return 0x12345678u;
}
# -target aarch64
a:
    mov w0, #22136
    movk w0, #4660, lsl #16
    ret
# -target arm
a:
    ldr r0, .LCPI0_0
    bx lr
.LCPI0_0:
    .long 305419896

XOM 和 PAN

永不具備特殊權限 (PAN) 是 ARM 晶片的安全功能 從核心模式存取使用者頁面的一般記憶體這有助於防範 核心無法接觸使用者記憶體,因此可能出現核心漏洞 搭配正常負載或商店指示使用作業系統必須改為啟用永久帳號 關閉,或按照 ldtrsttr 的操作說明存取這些網頁。PAN 為 目前未啟用 Fuchsia,但目前已有計畫支援 zircon pan-fxb

Aarch64 頁面表格項目具有 4 個相關位元以控制網頁權限。2 分 位元將用於使用者,且絕不會執行特權。並使用剩餘的兩個 描述這兩個存取層級的讀取和寫入頁面權限。一個 唯讀對應關係已移除讀取和寫入存取權,但允許使用者存取

ARMv8 參考手冊的這份表格顯示了可能的記憶體防護機制 僅使用 4 位元EL0 是使用者空間的例外狀況層級。資料列 0 和 2 示範如何建立使用者空間僅限執行的頁面。請參閱表格 D5-34 第 1 階段 。

UXN PXN AP[2:1] 較高例外狀況層級的存取權 透過 EL0 存取
0 1 00 R、W X
0 1 01 R、W R、W、X
0 1 10 R X
0 1 11 R R、X

遺憾的是,PAN 的演算法用於判斷網頁是否不應具有特殊權限 檢查是否方便存取從 PAN 的角度來看 僅限使用者執行的頁面似乎具有特殊權限的對應。這讓核心 存取使用者記憶體,因而略過 PAN 的 並將 PAN 和 XOM 不相容平移問題。這會導致 在未來使用 PAN 完全無法抵禦企圖利用 核心觸及使用者記憶體,不過在偵測 核心錯誤

這個問題會導致 Linux 和 Android 停止支援 XOM。這是 尤其是在 Android 無限期停止支援的 Android 使用者 11 之後加入 11,並設為 Android 10 中所有 aarch64 二進位檔的預設值 linux-revert。他們打算以硬體形式重新啟用這項功能 修正方式愈來愈普遍 加上外框

ARM 後來提議採用「強化」永久帳號 (ePAN) 的解決方案,更改永久帳號 (PAN) 不只能檢查使用者是否可讀取網頁,也無法檢查使用者可執行的作業。 不幸的是,這項功能的硬體可能無法用於任何 Fuchsia 目標 裝置。Linux 現已在 ePAN 原本設為 linux-re-land。我們無法對裝置上的 ePAN 提供支援 且 PAN 和 XOM 不相容時,應不會阻擋核心的 永久帳號導入。瞭解詳情

在圖 2 中,沒有可以讀取權限的設定 但卻從核心中移除唯一的例外狀況是 PAN,因此造成 當核心嘗試輕觸使用者可讀的頁面時,就會發生例外狀況。因此 您無法為核心建立僅供執行的對應,因為 核心無法將網頁執行檔標示為 EL1,但無法在讀取。因此 為使用者空間程序建立僅供執行的對應。

鎖定 XOM 硬體

ELF 中的區隔權限會指出執行程式碼所需的權限 正確。換句話說,如果系統建構 運作時所在的硬體,不一定可支援 XOM。相反地 如果 XOM 不需要讀取程式碼頁面,則無條件使用。取決於 OS 和載入器在系統最大程度上強制執行這些權限 允許 elf-segment-perm

虛擬記憶體權限

POSIX 指定 mmap 可能允許讀取 PROT_READ 網頁內容的讀取權限 尚未明確設定 posix-mmap。x86 和 macOS 上的 Linux 和 macOS 在 M1 晶片上,當僅使用 PROT_EXEC 根據 mmap 要求網頁時不會失敗 而是讓頁面變成 PROT_READ | PROT_EXEC。這些實作方式 系統會「盡力」處理使用者要求的系統呼叫。 另一方面,Fchsia 的系統呼叫始終會明確表達自己的需求 「 無法遵守」zx_vmar_* 系統呼叫不會以通知的方式提升 例如 POSIX 對應的網頁,正在提出要求 沒有 ZX_VM_PERM_READ 的網頁目前一律會失敗,因為這些硬體和 作業系統不支援沒有讀取權限的地圖對應頁面。優雅 轉換為支援僅限執行區隔和使用者空間的二進位檔 分配「僅限執行」記憶體的程式必須 OS 可以先對應僅限執行的網頁,再提出要求。

可讀取的程式碼安全性

許多攻擊主要都是透過閱讀 程式碼網頁中找到「小工具」或感興趣的可執行程式碼。位址空間 版面配置隨機化 (ASLR) 是作業系統 重複在程序位址空間中的半隨機位置使用。用於 由 Fuchsia 和其他許多作業系統提供,得以阻止攻擊,這些攻擊會仰賴知道程式碼的位置 或將資料儲存在記憶體中讓程式碼變成無法讀取的情況可進一步減少攻擊 途徑。

程式碼重複使用攻擊 (如「return-to-libc」rtl-attack) 用於傳回 對已知地址的函式控制。libc 是傳回或 加入,因為這個包含對攻擊者來說很實用的功能,且 因為這項程序極可能與 libc 連結。這已經 必須證明一般程式中可使用的小工具 打勾完成,讓對手能夠執行任意程式碼。

在許多情況下,對手的目標就是獲取殼層。ASLR 會 功能使用的位址不同,因此會更難發動攻擊 程式的叫用訊息。不過,ASLR 並非全面性的緩解措施 因為攻擊者可以讀取程式碼頁面 找出他們所使用的函式位址 無法藉由查看二進位檔中的地址得知XOM 完成 ASLR 無法以這種方式損毀,攻擊者也必須使用 另一種找出特定程式碼網頁的位置相關資訊的方法。

常見標記法

‘rwx/r-x/–x’

這些代表能對應至程序中的 ELF 區隔權限 取得對應的權限這種標記法經常用於 這包括說明檔案權限 以及依據 readelf。r、w 和 x 分別表示讀取、寫入和執行,而「-」代表 未授予權限。僅限執行的區隔會加上「--x」 授予其要求的權限。

R^X、W|X 等...

如上所述,R、W 和 X 指的是讀取、寫入和執行。「^」和「|」是 C 類語法 xor 和 或 運算子R^X 被讀取為「讀取 xor 執行」。

「ax」

這是組合器語法,可將區段標示為已配置和執行檔。 目前連結器會將「ax」區段放入「r-x」的區段。 lld 中的 --execute-only 標記會改為將這些片段標示為「--x」。

設計

為了提升使用者空間計畫的安全性,支援 XOM, 工具鍊和載入器需更新。Clang 驅動程式庫必須 將「--execute-only」標記傳給連結器,確保「ax」區段 而未對應至「r-x」區隔,則改為對應至「--x」區隔。 載入器也必須變更所有 權限必須包含至少讀取一次,因為此資訊不符現況。

XOM 只能在具備 ePAN 的硬體上使用, 為因應這項轉變,我們提供兩種選項:

  1. 變更 vmar_* 函式,以便盡量像許多 mmap 實作一樣
  2. 建立查詢核心的方法,前提是核心支援僅限執行的對應,以及 如果 XOM 不是,載入器將「--x」區隔的權限提升為「r-x」 廣告。
  3. 新增 ZX_VM_PERM_READ_IF_XOM_UNSUPPORTED 旗標,供載入器使用 「--x」區隔

在任何情況下,都可能會進行無訊息權限提升。 會是最簡單的方法,載入器需要 移除這類檢查第二種方式並未明顯 而是在載入器中新增一個簡單的檢查 對 OS 要求的記憶體權限第三個選項很實用 因此,使用者程式碼較不容易出錯

第一個選項會破壞 Fuchsia 現行的嚴格合約 使用者空間一律明確指出系統呼叫可以或無法遵守的項目。 第 2 個和第 3 個選項最終會導致記憶體權限處理不明確 載入 ELF 檔案時觸發。但這與 ELF 規格相符。區隔 權限不會指定 1:1 記憶體分配給 但記憶體至少要具備哪些權限 才能使程式正常運作ELF 載入器有權製作地圖 將「--x」區隔轉換為「r-x」記憶體 elf-segment-perm

破除 Fuchsia 現行明確系統呼叫合約的第一個選項 這並不是理想的做法。選項 2 和 3 都具有價值,且實作方式 將根據這兩個選項。

實作

系統呼叫新增

系統會新增 ZX_VM_PERM_READ_IF_XOM_UNSUPPORTED 標記,用來 在 options 中使用各種使用權限旗標的 zx_vmar_* 系統呼叫 如果不支援 XOM,會自動新增讀取權限。 ZX_VM_PERM_READ_IF_XOM_UNSUPPORTED 在邏輯上僅適用於 ZX_VM_PERM_EXEC 而非 ZX_VM_PERM_READ,但各種系統呼叫 接受此標記不會將其視為不變體。您可以放心將 ZX_VM_PERM_READ_IF_XOM_UNSUPPORTED 搭配任何其他標記組合,則 在其他情況下,則視為 ZX_VM_PERM_READ 無法對應僅執行的網頁。

系統將為以下項目新增kindZX_FEATURE_KIND_VMzx_system_get_features,這會產生類似如下的位元集 ZX_FEATURE_KIND_CPU。另外也會使用 ZX_VM_FEATURE_CAN_MAP_XOM。目前的實作項目一律會保留這個程式碼 位元 false,因為必須稍後才能啟用 XOM。這不會用於 載入器的原因是「r-x」記憶體權限適用於「--x」區段,但 仍然相當重要。

系統載入器 ABI 變更

目前和未來的載入器都會確保「--x」區隔可以載入到記憶體中 即使目標不支援 XOM 也無妨載入器會新增 對應僅執行區隔時,值為 ZX_VM_PERM_READ_IF_XOM_UNSUPPORTED

已傳送動態連接器 ABI 變更

同樣地,隨附於 SDK 的 Fuchsia libc 中的動態連結器也會 在為「--x」區段分配記憶體時,視需要提升權限 只在 ZX_VM_PERM_READ_IF_XOM_UNSUPPORTED

編譯器工具鍊變更

也會變更 clang 驅動程式庫,使其一律將 --execute-only 傳遞至 設為 aarch64-*-fuchsia 時的連結器。此外,我們也需要 最有可能的情況是,在 連結器,方便您在程式中輕鬆選擇不採用新的預設行為。

核心 XOM 實作

硬體送達支援 ePAN 後,核心即可為 記憶體頁面只含 ZX_VM_PERM_EXECUTEarm64 使用者副本 實作時可能需要更新,確保其與使用者記憶體的一致性 存取權受限應更新 user_copy 以使用 ldtrsttr 指示。確保使用者無法誘騙核心讀取內容 無法讀取的網頁此外,核心會對對應進行假設 某些地方可以閱讀,因此必須更改 或適當。這項作業會在之後完成。

不必要的變更

您不必變更 zx_process_read_memory,偵錯工具就能正常運作 通常在對僅供執行的二進位檔進行偵錯時,就會遇到以下錯誤。zx_process_read_memory忽略 並只會檢查瀏覽器 程序帳號代碼包含 ZX_RIGHT_READZX_RIGHT_WRITE

zx_vmar_protect 會繼續照常運作。最值得注意的是 這代表程序可以透過讀取權限保護自家程式碼頁面, 以備不時之需

成效

成效不會受到預期影響。

安全性

直到核心中導入 XOM 之前,含有「--x」區隔的二進位檔只會 使用「r-x」區段,將安全視為同等二進位檔案。系統支援 XOM 後 硬體和作業系統,這類程式會選擇使用僅限執行的記憶體 會提高安全性請參閱「程式碼權限」一節 頁數XOM 和 PAN可閱讀 程式碼安全性

隱私權

除了「安全性」一文中提到的問題以外,沒有其他需要注意的事項。

測試

zx_system_get_features 將進行小幅測試,以強制執行 XOM 支援在建構期間,我們預期 以便傳回內容。

系統會測試 ZX_VM_PERM_READ_IF_XOM_UNSUPPORTED 是否能生成頁面 可讀取 (如果 zx_system_get_features 回報,且 OS 無法讀取) 建立僅限執行的網頁。

同樣地,elfload 程式庫沒有任何真正的測試,儲存後即可執行模糊測試 系統不會測試預期的功能其功能本身 其他仰賴這項功能的元件進行測試請將測試新增至這裡 確保「--x」區隔已正確對應process_builder 程式庫 測試,確保能正確要求可讀取及執行 記憶體容量不足。

系統不會直接測試對目前動態連結器所做的變更。新的 動態連結器的規劃作業,需要進行大量測試,包括測試 共有「--x」區隔

Clang 驅動程式庫的變更會在上游 LLVM 中測試。

我們也會調整測試設定,以便在測試漫遊器上啟用 XOM 這類硬體沒有 ePAN,因此我們也不會啟用 XOM。這個 協助我們收進樹狀結構程式,在讀取程式碼頁面後需要選擇 也不必擔心

說明文件

系統會記錄您對 zx_system_get_features 所做的變更,以及 為何使用者空間想用這個類型查詢 ZX_VM_FEATURE_CAN_MAP_XOM。同樣地, 系統也會記錄 ZX_VM_PERM_READ_IF_XOM_UNSUPPORTED 標記。變更內容 各種載入器和 Clang 驅動程式庫預設值都不會記錄在 的職責。

缺點、替代方法、未知

我們對樹狀程式碼目前和未來有多少依賴可執行的元件,不知從何著手 確保程式碼清晰可讀可能是在文字中使用資料常數 手寫組件、從其他工具鍊或程式編譯的程式碼 自我檢查,無論如何,需要擁有可讀取程式碼頁面的程式, 仍能受益於共用程式庫依附元件 (包括 libc) 標示為只執行將 Clang 工具鍊變更為預設值 (僅限執行) 區隔將會破壞依賴可讀取程式碼的程式。沒有簡單的方法 檢查建構時間是否依賴這個行為。但一旦建立 發現某個程式需要「r-x」區段,請選擇不採用預設的「--x」 很簡單

對於需要讀取部分程式碼的程式而言 也無法輕鬆支援這項機制--execute-only linker 標記 將撤銷任何可執行區段的讀取權限,而且 將某個部分標示為需要閱讀。希望達到此行為的程式 就必須徹底選擇停用「僅限執行」功能

風險

Clang 驅動程式庫可能會預設使用 --execute-only 和程式碼 從「--x」區隔讀取到「--x」的片段,在硬體和核心之前都不會中斷 XOM 推出。這會造成潛在的未來相容性問題 沒有任何改變樹狀結構軟體中有測試, 而不是樹狀結構程式碼之外

既有作品和參考資料

因為在許多 POSIX 中處理 mmap 權限標記不明確 也不必模擬 zx_system_get_features(ZX_FEATURE_KIND_CAN_MAP_XOM, &feature)

Darwin 支援在較新的 Apple 晶片中使用 XOM,但採用其他更新 並具備專屬硬體功能晶片的晶片提供硬體支援 從核心和使用者記憶體去除個別權限位元。是 macOS 的使用者空間未啟用apple-xom