Android 10 包含 Android Live-LocK Daemon ( llkd
),旨在捕捉和緩解核心死鎖。 llkd
元件提供預設的獨立實現,但您也可以將llkd
程式碼整合到另一個服務中,作為主循環的一部分或作為單獨的執行緒。
檢測場景
llkd
有兩種偵測場景:持久 D 或 Z 狀態以及持久堆疊簽章。
持續 D 或 Z 狀態
如果執行緒處於 D(不間斷睡眠)或 Z(殭屍)狀態且沒有前進進度的時間超過ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms
,則llkd
會終止該進程(或父進程) )。如果後續掃描顯示相同的進程繼續存在, llkd
會確認活鎖情況,並以提供該情況的最詳細錯誤報告的方式使核心陷入恐慌。
llkd
包括一個自我看門狗,如果llkd
鎖定,它會發出警報;看門狗是流經主循環的預期時間的兩倍,並且採樣是每個ro.llk_sample_ms
。
持久堆疊簽名
對於 userdebug 版本, llkd
可以使用持久堆疊簽章檢查來偵測核心活鎖。如果處於 Z 以外的任何狀態的執行緒具有持久列出的ro.llk.stack
核心符號,且符號報告的時間長於ro.llk.timeout_ms
或ro.llk.stack.timeout_ms
,則llkd
會終止該進程(即使存在轉發)調度進度)。如果後續掃描顯示相同的進程繼續存在, llkd
會確認活鎖情況,並以提供該情況的最詳細錯誤報告的方式使核心陷入恐慌。
當存在即時鎖定條件時, lldk
檢查會持續持續,並在 Linux 上的/proc/pid/stack
檔案中尋找組成的字串" symbol+0x"
或" symbol.cfi+0x"
。符號清單位於ro.llk.stack
中,預設為「 cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
」的逗號分隔清單。
符號應該是稀有且短暫的,以便在典型的系統上,在ro.llk.stack.timeout_ms
的超時時間內,該函數只能在樣本中看到一次(樣本每隔ro.llk.check_ms
出現一次) 。由於缺乏 ABA 保護,這是防止誤觸發的唯一方法。符號函數必須出現在呼叫可能競爭的鎖的函數下方。如果鎖位於符號函數下方或之中,則該符號會出現在所有受影響的進程中,而不僅僅是導致鎖定的進程中。
覆蓋範圍
llkd
的預設實作不會監視init
、 [kthreadd]
或[kthreadd]
產生。對於llkd
覆蓋[kthreadd]
產生的執行緒:
- 驅動程式不得保持持續 D 狀態,
或者
- 如果執行緒被外部終止,驅動程式必須具有恢復執行緒的機制。例如,使用
wait_event_interruptible()
而不是wait_event()
。
如果滿足上述條件之一,則可以調整llkd
黑名單以覆蓋核心元件。堆疊符號檢查涉及額外的進程黑名單,以防止阻止ptrace
操作的服務違反 sepolicy。
安卓屬性
llkd
響應多個 Android 屬性(如下所列)。
- 名為
prop_ms
屬性以毫秒為單位。 - 對清單使用逗號 (,) 分隔符號的屬性使用前導分隔符號來保留預設條目,然後分別使用可選的加號 (+) 和減號 (-) 前綴添加或減去條目。對於這些列表,字串「false」與空列表同義,空白或缺失的條目將採用指定的預設值。
ro.config.low_ram
設備配置的記憶體有限。
ro.debuggable
設備配置為 userdebug 或 eng 建置。
ro.llk.sysrq_t
如果屬性為“eng”,則預設值不是ro.config.low_ram
或ro.debuggable
。如果為 true,則轉儲所有執行緒 ( sysrq t
)。
滾動啟用
允許啟用活鎖守護程式。預設為 false。
llk.啟用
評估工程版本。預設為ro.llk.enable
。
ro.khungtask.enable
允許啟用[khungtask]
守護程式。預設為 false。
khungtask.enable
評估工程版本。預設為ro.khungtask.enable
。
ro.llk.mlockall
啟用對mlockall()
的呼叫。預設為 false。
ro.khungtask.timeout
[khungtask]
最大時間限制。預設值為 12 分鐘。
ro.llk.timeout_ms
D 或 Z 最大時間限制。預設值為 10 分鐘。將此值加倍以設定llkd
的警報看門狗。
ro.llk.D.timeout_ms
D 最大時間限制。預設為ro.llk.timeout_ms
。
ro.llk.Z.timeout_ms
Z最大時間限制。預設為ro.llk.timeout_ms
。
ro.llk.stack.timeout_ms
檢查持久堆疊符號的最大時間限制。預設為ro.llk.timeout_ms
。僅在 userdebug 或 eng 版本上有效。
ro.llk.check_ms
D 或 Z 的線程樣本。預設為兩分鐘。
ro.llk.stack
檢查核心堆疊符號,如果持續存在則表示子系統已鎖定。預設值為cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable
逗號分隔的核心符號清單。此檢查不會進行前向調度 ABA,除非在ro.llk.stack.timeout_ms
期間輪詢每個ro.llk_check_ms
,因此堆疊符號應該異常罕見且短暫(符號不太可能在所有情況下持續顯示)堆疊的樣本)。檢查堆疊擴充中" symbol+0x"
或" symbol.cfi+0x"
是否符合。僅適用於 userdebug 或 eng 版本;使用者建置的安全性問題導致權限受限,從而阻止了此檢查。
ro.llk.黑名單進程
llkd
不監視指定的進程。預設值為0,1,2
( kernel
、 init
和[kthreadd]
)加上進程名稱init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1]
。進程可以是comm
、 cmdline
或pid
引用。自動預設值可以大於目前最大屬性大小 92。
ro.llk.blacklist.parent
llkd
不會監視具有指定父進程的進程。預設值為0,2,adbd&[setsid]
( kernel
、 [kthreadd]
和adbd
僅適用於殭屍setsid
)。與號 (&) 分隔符號指定僅在與目標子程序組合時忽略父進程。選擇&符號是因為它從來不是進程名稱的一部分;然而,shell 中的setprop
要求對 & 符號進行轉義或引用,儘管通常指定該符號的init rc
檔案不存在此問題。父行程或目標行程可以是comm
、 cmdline
或pid
引用。
ro.llk.黑名單.uid
llkd
不會監視與指定 uid 相符的進程。以逗號分隔的 uid 編號或名稱清單。預設為空或 false。
ro.llk.blacklist.process.stack
llkd
不會監視指定進程子集的即時鎖定堆疊簽章。預設為進程名稱init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd
。防止與阻止ptrace
的進程相關的 sepolicy 違規(因為無法檢查這些進程)。僅在 userdebug 和 eng 版本上有效。有關構建類型的詳細信息,請參閱構建 Android 。
建築問題
- 屬性限制為 92 個字元(但是,對於來源中的
include/llkd.h
檔案中定義的預設值,這將被忽略)。 - 內建的
[khungtask]
守護程式過於通用,並且過度依賴處於 D 狀態的驅動程式程式碼。切換到 S 將使任務可終止(如果需要,可由驅動程式復活)。
庫介面(可選)
您可以選擇使用libllkd
元件中的下列 C 介面將llkd
合併到另一個特權守護程式:
#include "llkd.h"
bool llkInit(const char* threadname) /* return true if enabled */
unsigned llkCheckMillseconds(void) /* ms to sleep for next check */
如果提供了線程名,則會自動產生一個線程,否則呼叫者必須在其主循環中呼叫llkCheckMilliseconds
。此函數傳回下一次預期呼叫此處理程序之前的時間段。