Arm v9 引入了 Arm內存標記擴展(MTE),這是一種標記內存的硬件實現。
在高層次上,MTE 用額外的元數據標記每個內存分配/釋放。它將標籤分配給內存位置,然後可以將其與引用該內存位置的指針相關聯。在運行時,CPU 檢查指針和元數據標籤在每次加載和存儲時是否匹配。
在 Android 12 中,內核和用戶空間堆內存分配器可以使用元數據擴充每個分配。這有助於檢測釋放後使用和緩衝區溢出錯誤,這是我們代碼庫中最常見的內存安全錯誤來源。
MTE 操作模式
MTE 具有三種操作模式:
- 同步模式 (SYNC)
- 異步模式 (ASYNC)
- 非對稱模式 (ASYMM)
同步模式 (SYNC)
此模式針對錯誤檢測的正確性進行了優化而不是性能,並且可以用作精確的錯誤檢測工具,當更高的性能開銷可以接受時。啟用後,MTE SYNC 充當安全緩解措施。在標籤不匹配時,處理器立即中止執行並使用SIGSEGV
(代碼SEGV_MTESERR
)和有關內存訪問和錯誤地址的完整信息終止進程。
我們建議在測試期間使用此模式作為 HWASan/KASAN 的替代方案,或者在目標進程代表易受攻擊的攻擊面時在生產中使用此模式。此外,當 ASYNC 模式指示存在錯誤時,可以通過使用運行時 API 將執行切換到 SYNC 模式來獲得準確的錯誤報告。
在 SYNC 模式下運行時, Android 分配器會記錄所有分配和釋放的堆棧跟踪,並使用它們來提供更好的錯誤報告,其中包括對內存錯誤的解釋,例如 use-after-free 或 buffer-overflow,以及堆棧相關記憶事件的痕跡。此類報告提供更多上下文信息,並使錯誤更易於跟踪和修復。
異步模式 (ASYNC)
此模式針對錯誤報告的準確性進行了優化,可用作內存安全錯誤的低開銷檢測。
在標籤不匹配時,處理器繼續執行直到最近的內核入口(例如,系統調用或計時器中斷),在那裡它使用SIGSEGV
(代碼SEGV_MTEAERR
)終止進程,而不記錄錯誤地址或內存訪問。
我們建議在經過良好測試的代碼庫的生產環境中使用這種模式,在這些代碼庫中已知內存安全漏洞的密度很低,這是通過在測試期間使用 SYNC 模式來實現的。
非對稱模式 (ASYMM)
Arm v8.7-A 中的一項附加功能是,非對稱 MTE 模式提供對內存讀取的同步檢查和對內存寫入的異步檢查,其性能類似於 ASYNC 模式。在大多數情況下,此模式是對 ASYNC 模式的改進,我們建議在可用時使用它而不是 ASYNC。
因此,以下描述的 API 均未提及非對稱模式。相反,可以將操作系統配置為在請求異步時始終使用非對稱模式。有關詳細信息,請參閱“配置特定於 CPU 的首選 MTE 級別”部分。
用戶空間中的 MTE
以下部分描述瞭如何為系統進程和應用程序啟用 MTE。默認情況下禁用 MTE,除非為特定進程設置了以下選項之一(請參閱下面為哪些組件啟用了 MTE)。
使用構建系統啟用 MTE
作為進程範圍的屬性,MTE 由主可執行文件的構建時間設置控制。以下選項允許為單個可執行文件或源樹中的整個子目錄更改此設置。該設置在庫或任何既不是可執行文件也不是測試的目標上會被忽略。
1. 在Android.bp
中啟用 MTE(示例),針對特定項目:
MTE 模式 | 環境 |
---|---|
異步 MTE | sanitize: { memtag_heap: true, } |
同步 MTE | sanitize: { memtag_heap: true, diag: { memtag_heap: true, }, } |
或在Android.mk:
MTE 模式 | 環境 |
---|---|
Asynchronous MTE | LOCAL_SANITIZE := memtag_heap |
Synchronous MTE | LOCAL_SANITIZE := memtag_heap LOCAL_SANITIZE_DIAG := memtag_heap |
2. 使用產品變量在源樹中的子目錄上啟用 MTE:
MTE模式 | 包括列表 | 排除列表 |
---|---|---|
異步 | PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS MEMTAG_HEAP_ASYNC_INCLUDE_PATHS | PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS |
同步 | PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
或者
MTE 模式 | 環境 |
---|---|
異步 MTE | MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
同步 MTE | MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
或通過指定可執行文件的排除路徑:
MTE 模式 | 環境 |
---|---|
異步 MTE | PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS |
同步 MTE |
示例,(類似於PRODUCT_CFI_INCLUDE_PATHS
的用法)
RODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor) PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \ vendor/$(vendor)/projectB
使用系統屬性啟用 MTE
通過設置以下系統屬性,可以在運行時覆蓋上述構建設置:
arm64.memtag.process.<basename> = (off|sync|async)
其中basename
代表可執行文件的基本名稱。
例如,要將/system/bin/ping
或/data/local/tmp/ping
設置為使用異步 MTE,請使用adb shell setprop arm64.memtag.process.ping async
。
使用環境變量啟用 MTE
覆蓋構建設置的另一種方法是定義環境變量: MEMTAG_OPTIONS=(off|sync|async)
如果定義了環境變量和系統屬性,則變量優先。
為應用程序啟用 MTE
如果未指定,默認情況下禁用 MTE,但想要使用 MTE 的應用程序可以通過在AndroidManifest.xml
中的<application>
或<process>
標記下設置android:memtagMode
來實現。
android:memtagMode=(off|default|sync|async)
在<application>
標記上設置時,該屬性會影響應用程序使用的所有進程,並且可以通過設置<process>
標記為單個進程覆蓋。
對於實驗,兼容性更改可用於為未在清單中指定任何值(或指定default
)的應用程序設置memtagMode
屬性的默認值。
這些可以在全局設置菜單中的System > Advanced > Developer options > App Compatibility Changes
下找到。設置NATIVE_MEMTAG_ASYNC
或NATIVE_MEMTAG_SYNC
可為特定應用程序啟用 MTE。
或者,可以使用am
命令設置,如下所示:
$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name
構建 MTE 系統映像
我們強烈建議在開發和啟動期間對所有本機二進製文件啟用 MTE。如果在測試版本中啟用,這有助於及早檢測內存安全錯誤並提供真實的用戶覆蓋。
我們強烈建議在開發期間對所有本機二進製文件啟用同步模式的 MTE
SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m
與構建系統中的任何變量一樣, SANITIZE_TARGET
可以用作環境變量或make
設置(例如,在product.mk
文件中)。
請注意,這將為所有本機進程啟用 MTE,但不適用於可以按照上述說明啟用 MTE 的應用程序(從zygote64
派生)。
配置特定於 CPU 的首選 MTE 級別
在某些 CPU 上,MTE 在 ASYMM 甚至 SYNC 模式下的性能可能與 ASYNC 相似。這使得在請求不太嚴格的檢查模式時對這些 CPU 啟用更嚴格的檢查是值得的,以便在不降低性能的情況下獲得更嚴格檢查的錯誤檢測優勢。
默認情況下,配置為以 ASYNC 模式運行的進程將在所有 CPU 上以 ASYNC 模式運行。要將內核配置為在特定 CPU 上以 SYNC 模式運行這些進程,必須在引導時將值 sync 寫入sysfs
條目/sys/devices/system/cpu/cpu<N>/mte_tcf_preferred
。這可以通過初始化腳本來完成。例如,要將 CPU 0-1 配置為以 SYNC 模式運行 ASYNC 模式進程,並將 CPU 2-3 配置為以 ASYMM 模式運行,可以將以下內容添加到供應商 init 腳本的 init 子句中:
write /sys/devices/system/cpu/cpu0/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu1/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu2/mte_tcf_preferred asymm write /sys/devices/system/cpu/cpu3/mte_tcf_preferred asymm
在 SYNC 模式下運行的 ASYNC 模式進程的墓碑將包含內存錯誤位置的精確堆棧跟踪。但是,它們不包括分配或釋放堆棧跟踪。僅當進程配置為在 SYNC 模式下運行時,這些堆棧跟踪才可用。
int mallopt(M_THREAD_DISABLE_MEM_INIT, level)
其中level
為 0 或 1。
禁用 malloc 中的內存初始化,並避免更改內存標籤,除非需要正確性。
int mallopt(M_MEMTAG_TUNING, level)
其中level
是:
-
M_MEMTAG_TUNING_BUFFER_OVERFLOW
-
M_MEMTAG_TUNING_UAF
選擇標籤分配策略。
- 默認設置是
M_MEMTAG_TUNING_BUFFER_OVERFLOW
。 -
M_MEMTAG_TUNING_BUFFER_OVERFLOW
- 通過將不同的標記值分配給相鄰的分配來實現對線性緩衝區溢出和下溢錯誤的確定性檢測。此模式檢測釋放後使用錯誤的機會略有降低,因為每個內存位置只有一半可能的標記值可用。請記住,MTE 無法檢測同一個標籤顆粒(16 字節對齊的塊)內的溢出,即使在這種模式下也可能會漏掉小的溢出。這種溢出不會是內存損壞的原因,因為一個顆粒內的內存從不用於多次分配。 -
M_MEMTAG_TUNING_UAF
- 啟用獨立隨機標籤,以統一 ~93% 的概率檢測空間(緩衝區溢出)和時間(釋放後使用)錯誤。
除了上述 API 之外,有經驗的用戶可能還想了解以下內容:
- 設置
PSTATE.TCO
硬件寄存器可以暫時抑制標籤檢查(示例)。例如,複製帶有未知標籤內容的內存範圍,或解決熱循環中的性能瓶頸時。 - 使用
M_HEAP_TAGGING_LEVEL_SYNC
時,系統崩潰處理程序會提供額外的信息,例如分配和解除分配堆棧跟踪。此功能需要訪問標記位,並通過在設置信號處理程序時傳遞SA_EXPOSE_TAGBITS
標誌來啟用。任何設置自己的信號處理程序並將未知崩潰委託給系統的程序都建議這樣做。
內核中的 MTE
要為內核啟用 MTE 加速 KASAN,請使用CONFIG_KASAN=y
、 CONFIG_KASAN_HW_TAGS=y
配置內核。從Android 12-5.10
開始,這些配置默認在 GKI 內核上啟用。
這可以在引導時使用以下命令行參數進行控制:
-
kasan=[on|off]
- 啟用或禁用 KASAN(默認值:on
) -
kasan.mode=[sync |async ]
- 在同步和異步模式之間進行選擇(默認值:sync
) -
kasan.stacktrace=[on|off]
- 是否收集堆棧跟踪(默認值:on
)- 堆棧跟踪收集還需要
stack_depot_disable=off
。
- 堆棧跟踪收集還需要
-
kasan.fault=[report|panic]
- 是否只打印報告,或者也使內核恐慌(默認值:report
)。無論此選項如何,標籤檢查都會在第一次報告錯誤後被禁用。
推薦使用
我們強烈建議在啟動、開發和測試期間使用 SYNC 模式。應該為使用環境變量或構建系統的所有進程全局啟用此選項。在這種模式下,可以在開發過程的早期檢測到錯誤,代碼庫可以更快地穩定下來,並且可以避免在生產後期檢測錯誤的成本。
我們強烈建議在生產中使用 ASYNC 模式。這提供了一種低開銷工具,用於檢測進程中是否存在內存安全漏洞以及進一步的深度防禦。一旦檢測到錯誤,開發人員可以利用運行時 API 切換到 SYNC 模式,並從一組抽樣用戶那裡獲得準確的堆棧跟踪。
我們強烈建議為 SoC 配置特定於 CPU 的首選 MTE 級別。 Asymm 模式通常具有與 ASYNC 相同的性能特徵,並且幾乎總是比它更可取。小型有序內核通常在所有三種模式下表現出相似的性能,並且可以配置為首選 SYNC。
開發人員應通過檢查/data/tombstones
、 logcat
或通過監視供應商DropboxManager
管道是否存在最終用戶錯誤來檢查是否存在崩潰。有關調試 Android 本機代碼的更多信息,請參閱此處的信息。
支持 MTE 的平台組件
在 Android 12 中,許多安全關鍵系統組件使用 MTE ASYNC 來檢測最終用戶崩潰並充當額外的縱深防禦層。這些組件是:
- 網絡守護進程和實用程序(除了
netd
) - 藍牙、SecureElement、NFC HAL 和系統應用
statsd
守護進程system_server
-
zygote64
(允許應用程序選擇使用 MTE)
這些目標是根據以下標準選擇的:
- 特權進程(定義為有權訪問 unprivileged_app SELinux 域無法訪問的內容的進程)
- 處理不可信的輸入(二規則)
- 可接受的性能減速(減速不會產生用戶可見的延遲)
我們鼓勵供應商按照上述標准在生產中為更多組件啟用 MTE。在開發過程中,我們建議使用 SYNC 模式測試這些組件,以檢測容易修復的錯誤,並評估 ASYNC 對其性能的影響。
未來,Android 計劃根據即將推出的硬件設計的性能特徵,擴展啟用 MTE 的系統組件列表。