Demon blokady Live-LocK w systemie Android (llkd)

Android 10 zawiera demona Android Live-LocK ( llkd ), którego zadaniem jest wychwytywanie i łagodzenie zakleszczeń jądra. Komponent llkd zapewnia domyślną, samodzielną implementację, ale alternatywnie można zintegrować kod llkd z inną usługą, jako część głównej pętli lub jako oddzielny wątek.

Scenariusze wykrywania

llkd ma dwa scenariusze wykrywania: trwały stan D lub Z oraz trwały podpis stosu.

Trwały stan D lub Z

Jeśli wątek znajduje się w stanie D (nieprzerwanego uśpienia) lub Z (zombie) i nie ma postępu w przód przez czas dłuższy niż ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms , llkd kończy proces (lub proces nadrzędny ). Jeśli kolejne skanowanie wykaże, że ten sam proces nadal istnieje, llkd potwierdza stan blokady na żywo i wywołuje panikę w jądrze w sposób zapewniający najbardziej szczegółowy raport o błędzie dla tego warunku.

llkd zawiera funkcję samoobsługową, która alarmuje, jeśli llkd się blokuje; watchdog jest dwukrotnie dłuższy od oczekiwanego czasu przepływu przez pętlę główną, a próbkowanie odbywa się co ro.llk_sample_ms .

Trwały podpis stosu

W przypadku wersji userdebug, llkd może wykryć blokady jądra przy użyciu trwałego sprawdzania podpisów stosu. Jeśli wątek w jakimkolwiek stanie z wyjątkiem Z ma na liście trwały symbol jądra ro.llk.stack , który jest raportowany dłużej niż ro.llk.timeout_ms lub ro.llk.stack.timeout_ms , llkd kończy proces (nawet jeśli istnieje postęp planowania). Jeśli kolejne skanowanie wykaże, że ten sam proces nadal istnieje, llkd potwierdza stan blokady na żywo i wywołuje panikę w jądrze w sposób zapewniający najbardziej szczegółowy raport o błędzie dla tego warunku.

Kontrola lldk jest kontynuowana w sposób ciągły, gdy istnieje warunek blokady na żywo i szuka utworzonych ciągów " symbol+0x" lub " symbol.cfi+0x" w pliku /proc/pid/stack w systemie Linux. Lista symboli znajduje się w ro.llk.stack i domyślnie jest to lista rozdzielona przecinkami „ cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable ”.

Symbole powinny być rzadkie i na tyle krótkotrwałe, aby w typowym systemie funkcja była widoczna tylko raz w próbce przez limit czasu ro.llk.stack.timeout_ms (próbki pojawiają się co ro.llk.check_ms ). Ze względu na brak zabezpieczenia ABA jest to jedyny sposób zapobiegania fałszywemu wyzwalaniu. Funkcja symboliczna musi pojawić się poniżej funkcji wywołującej blokadę, która może konkurować. Jeśli blokada znajduje się poniżej lub w funkcji symbolu, symbol pojawia się we wszystkich procesach, których to dotyczy, a nie tylko w tym, który spowodował zablokowanie.

Zasięg

Domyślna implementacja llkd nie monitoruje pojawiania się init , [kthreadd] ani [kthreadd] . Aby llkd obejmował wątki utworzone przez [kthreadd] :

  • Kierowcy nie mogą pozostawać w trwałym stanie D,

LUB

  • Sterowniki muszą mieć mechanizmy umożliwiające odzyskanie wątku w przypadku jego zabicia zewnętrznie. Na przykład użyj wait_event_interruptible() zamiast wait_event() .

Jeśli spełniony jest jeden z powyższych warunków, czarną listę llkd można dostosować tak, aby obejmowała komponenty jądra. Sprawdzanie symboli stosu obejmuje dodatkową czarną listę procesów, która zapobiega naruszeniom zasad separacyjnych w usługach blokujących operacje ptrace .

Właściwości Androida

llkd reaguje na kilka właściwości Androida (wymienionych poniżej).

  • Właściwości o nazwie prop_ms są podawane w milisekundach.
  • Właściwości, które używają separatora przecinka (,) dla list, używają separatora początkowego, aby zachować wpis domyślny, a następnie dodają lub odejmuje wpisy z opcjonalnymi przedrostkami odpowiednio plus (+) i minus (-). W przypadku tych list ciąg „false” jest równoznaczny z pustą listą, a puste lub brakujące wpisy korzystają z określonej wartości domyślnej.

ro.config.low_ram

Urządzenie zostało skonfigurowane z ograniczoną pamięcią.

ro.debugowalne

Urządzenie jest skonfigurowane do debugowania użytkownika lub kompilacji eng.

ro.llk.sysrq_t

Jeśli właściwość to „eng”, wartością domyślną nie jest ro.config.low_ram ani ro.debuggable . Jeśli to prawda, zrzuć wszystkie wątki ( sysrq t ).

ro.llk.enable

Zezwól na włączenie demona blokady na żywo. Wartość domyślna to fałsz.

llk.włącz

Oceniony pod kątem kompilacji eng. Wartość domyślna to ro.llk.enable .

ro.khungtask.enable

Zezwól na włączenie demona [khungtask] . Wartość domyślna to fałsz.

khungtask.włącz

Oceniony pod kątem kompilacji eng. Wartość domyślna to ro.khungtask.enable .

ro.llk.mlockall

Włącz wywołanie mlockall() . Wartość domyślna to fałsz.

ro.khungtask.timeout

[khungtask] maksymalny limit czasu. Wartość domyślna to 12 minut.

ro.llk.timeout_ms

Maksymalny limit czasu D lub Z. Wartość domyślna to 10 minut. Podwój tę wartość, aby ustawić watchdog alarmu dla llkd .

ro.llk.D.timeout_ms

D maksymalny limit czasu. Wartość domyślna to ro.llk.timeout_ms .

ro.llk.Z.timeout_ms

Z maksymalny limit czasu. Wartość domyślna to ro.llk.timeout_ms .

ro.llk.stack.timeout_ms

Sprawdza maksymalny limit czasu symboli stosu trwałego. Wartość domyślna to ro.llk.timeout_ms . Aktywne tylko w przypadku debugowania użytkownika lub kompilacji eng .

ro.llk.check_ms

Przykłady wątków dla D lub Z. Wartość domyślna to dwie minuty.

ro.llk.stack

Sprawdza symbole stosu jądra, które, jeśli są stale obecne, mogą wskazywać, że podsystem jest zablokowany. Wartość domyślna to cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable lista symboli jądra rozdzielonych przecinkami. Kontrola nie wykonuje planowania ABA w przód, z wyjątkiem odpytywania co ro.llk_check_ms w okresie ro.llk.stack.timeout_ms , więc symbole stosu powinny być wyjątkowo rzadkie i ulotne (jest bardzo mało prawdopodobne, aby symbol pojawiał się stale we wszystkich próbki stosu). Sprawdza dopasowanie dla " symbol+0x" lub " symbol.cfi+0x" podczas rozszerzania stosu. Dostępne tylko w kompilacjach userdebug lub eng ; obawy dotyczące bezpieczeństwa kompilacji użytkowników powodują ograniczone uprawnienia, które uniemożliwiają tę kontrolę.

ro.llk.blacklist.process

llkd nie obserwuje określonych procesów. Wartość domyślna to 0,1,2 ( kernel , init i [kthreadd] ) plus nazwy procesów init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1] . Proces może być odwołaniem comm , cmdline lub pid . Automatyczna wartość domyślna może być większa niż bieżący maksymalny rozmiar właściwości wynoszący 92.

ro.llk.blacklist.parent

llkd nie obserwuje procesów, które mają określonych rodziców. Wartość domyślna to 0,2,adbd&[setsid] ( kernel , [kthreadd] i adbd tylko w przypadku setsid zombie). Separator ampersand (&) określa, że ​​element nadrzędny jest ignorowany tylko w połączeniu z docelowym procesem potomnym. Wybrano znak ampersand, ponieważ nigdy nie jest on częścią nazwy procesu; jednakże setprop w powłoce wymaga znaku ampersanda umieszczonego w cudzysłowie lub znaku ucieczki, chociaż w pliku init rc , w którym jest to zwykle określone, ten problem nie występuje. Procesem nadrzędnym lub docelowym może być odwołanie do comm , cmdline lub pid .

ro.llk.blacklist.uid

llkd nie obserwuje procesów pasujących do określonych identyfikatorów UID. Rozdzielana przecinkami lista numerów UID lub nazw. Wartość domyślna jest pusta lub fałszywa.

ro.llk.blacklist.process.stack

llkd nie monitoruje określonego podzbioru procesów pod kątem podpisów stosu blokad na żywo. Domyślnie są to nazwy procesów init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd . Zapobiega naruszeniom sepolicy związanym z procesami blokującymi ptrace (ponieważ nie można ich sprawdzić). Aktywne tylko w przypadku debugowania użytkownika i kompilacji eng . Aby uzyskać szczegółowe informacje na temat typów kompilacji, zobacz Tworzenie systemu Android .

Obawy architektoniczne

  • Właściwości są ograniczone do 92 znaków (jednak jest to ignorowane w przypadku wartości domyślnych zdefiniowanych w pliku include/llkd.h w źródłach).
  • Wbudowany demon [khungtask] jest zbyt ogólny i powoduje zbyt częste uruchamianie kodu sterownika, który znajduje się w stanie D. Przełączenie na S spowodowałoby, że zadania można byłoby zabić (i w razie potrzeby wskrzesić je kierowcy).

Interfejs biblioteki (opcjonalnie)

Opcjonalnie możesz włączyć llkd do innego uprzywilejowanego demona, korzystając z następującego interfejsu C z komponentu libllkd :

#include "llkd.h"
bool llkInit(const char* threadname) /* return true if enabled */
unsigned llkCheckMillseconds(void)   /* ms to sleep for next check */

Jeśli zostanie podana nazwa wątku, wątek pojawi się automatycznie, w przeciwnym razie osoba wywołująca będzie musiała wywołać llkCheckMilliseconds w swojej głównej pętli. Funkcja zwraca okres czasu przed następnym oczekiwanym wywołaniem tej procedury obsługi.