AArch64 二進製文件的只執行內存 (XOM)

AArch64 系統二進製文件的可執行代碼部分默認標記為僅執行(不可讀),作為針對即時代碼重用攻擊的強化緩解措施。將數據和代碼混合在一起的代碼以及有目的地檢查這些部分的代碼(沒有首先將內存段重新映射為可讀)不再起作用。如果應用程序嘗試讀取內存中啟用了僅執行內存 (XOM) 的系統庫的代碼段,而沒有先將該段標記為可讀,則目標 SDK 為 10(API 級別 29 或更高)的應用程序會受到影響。

要充分受益於這種緩解措施,需要硬件和內核支持。如果沒有這種支持,緩解措施可能只能部分實施。 Android 4.9 通用內核包含適當的補丁程序,以便在 ARMv8.2 設備上提供對此的全面支持。

執行

編譯器生成的 AArch64 二進製文件假定代碼和數據沒有混合。啟用此功能不會對設備的性能產生負面影響。

對於必須對其可執行段執行有意內存自省的代碼,建議對需要檢查的代碼段調用mprotect以使其可讀,然後在檢查完成時刪除可讀性。
此實現會導致讀取標記為僅執行的內存段,從而導致分段錯誤 ( SEGFAULT )。這可能是由於錯誤、漏洞、與代碼混合的數據(文字池)或有意的內存自省而發生的。

設備支持和影響

具有較早硬件或較早內核(低於 4.9)但沒有所需補丁的設備可能無法完全支持或受益於此功能。沒有內核支持的設備可能不會強制用戶訪問只執行內存,但是顯式檢查頁面是否可讀的內核代碼仍然可以強制執行此屬性,例如process_vm_readv()

必須在內核中設置內核標誌CONFIG_ARM64_UAO以確保內核尊重標記為僅執行的用戶空間頁面。早期的 ARMv8 設備或禁用了用戶訪問覆蓋 (UAO) 的 ARMv8.2 設備可能無法完全從中受益,並且可能仍然能夠使用系統調用讀取僅執行頁面。

重構現有代碼

從 AArch32 移植的代碼可能包含混合的數據和代碼,從而導致出現問題。在許多情況下,解決這些問題就像將常量移動到程序集文件中的.data部分一樣簡單。

手寫程序集可能需要重構以分離本地池常量。

例子:

Clang 編譯器生成的二進製文件在代碼中混合數據時應該沒有問題。如果包含 GNU 編譯器集合 (GCC) 生成的代碼(來自靜態庫),請檢查輸出二進製文件以確保未將常量匯集到代碼段中。

如果可執行代碼段需要代碼自省,首先調用mprotect將代碼標記為可讀。然後在操作完成後,再次調用mprotect將其標記為不可讀。

啟用

默認情況下,對構建系統中的所有 64 位二進製文件啟用僅執行。

禁用

您可以在模塊級別、整個子目錄樹或全局禁用整個構建的僅執行。

通過將LOCAL_XOMxom變量設置為false ,可以為無法重構或需要讀取其可執行代碼的單個模塊禁用 XOM。

// Android.mk
LOCAL_XOM := false

// Android.bp
cc_binary { // or other module types
   ...
   xom: false,
}

如果在靜態庫中禁用了只執行內存,則構建系統會將其應用於該靜態庫的所有依賴模塊。您可以使用xom: true,來覆蓋它。

要禁用特定子目錄(例如 foo/bar/)中的僅執行內存,請將值傳遞給XOM_EXCLUDE_PATHS

make -j XOM_EXCLUDE_PATHS=foo/bar

或者,您可以在產品配置中設置PRODUCT_XOM_EXCLUDE_PATHS變量。

您可以通過將ENABLE_XOM=false傳遞給您的make命令來全局禁用只執行二進製文件。

make -j ENABLE_XOM=false

驗證

沒有可用於只執行內存的 CTS 或驗證測試。您可以使用readelf手動驗證二進製文件並檢查段標誌。