Android Live-Lock 守護程式 (llkd)

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_msro.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_ramro.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,2kernelinit[kthreadd] )加上進程名稱init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1] 。進程可以是commcmdlinepid引用。自動預設值可以大於目前最大屬性大小 92。

ro.llk.blacklist.parent

llkd不會監視具有指定父進程的進程。預設值為0,2,adbd&[setsid]kernel[kthreadd]adbd僅適用於殭屍setsid )。與號 (&) 分隔符號指定僅在與目標子程序組合時忽略父進程。選擇&符號是因為它從來不是進程名稱的一部分;然而,shell 中的setprop要求對 & 符號進行轉義或引用,儘管通常指定該符號的init rc檔案不存在此問題。父行程或目標行程可以是commcmdlinepid引用。

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 。此函數傳回下一次預期呼叫此處理程序之前的時間段。