Demon blokady na żywo na Androidzie (llkd)

Android 10 zawiera demona Androida do blokowania na żywo (llkd), który ma za zadanie wykrywać i łagodzić blokady jądra. Komponent llkd zapewnia domyślne samodzielne wdrożenie, ale możesz też zintegrować kod llkd z inną usługą, np. jako część głównej pętli lub jako osobny wątek.

Scenariusze wykrywania

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

Stały stan D lub Z

Jeśli wątek jest w stanie D (nieprzerwany sen) lub Z (nieśmiertelny), a jego postęp nie zmienia się od dłuższego czasu (dłużej niż ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms), llkd zabija proces (lub proces nadrzędny). Jeśli kolejne skanowanie wykaże, że ten sam proces nadal występuje, llkd potwierdzi zablokowanie i spowoduje panikę w jądrze, co spowoduje wygenerowanie najbardziej szczegółowego raportu o błędzie dla tego stanu.

llkd zawiera mechanizm samokontroli, który ostrzega, jeśli llkd się zawiesi. Czas działania mechanizmu jest podwojony w stosunku do oczekiwanego czasu przepływu przez główną pętlę, a próbkowanie odbywa się co ro.llk_sample_ms.

Podpis trwałego stosu

W przypadku wersji userdebug może ona wykrywać blokady na żywo w jądrze za pomocą sprawdzania podpisu stosu.llkd Jeśli wątek w dowolnym stanie innym niż Z ma trwały symbol jądra ro.llk.stack, który jest zgłaszany dłużej niż ro.llk.timeout_ms lub ro.llk.stack.timeout_ms, llkd zabija proces (nawet jeśli jest w drodze do przodu). Jeśli kolejne skanowanie wykaże, że ten sam proces nadal występuje, llkd potwierdzi zablokowanie i spowoduje panikę w jądrze, co spowoduje wygenerowanie najbardziej szczegółowego raportu o błędzie dla tego stanu.

Sprawdzanie lldk jest ciągłe, gdy istnieje warunek blokady na żywo, i szuka w pliku /proc/pid/stack w systemie Linux ciągu znaków symbol+0x lub symbol.cfi+0x. Lista symboli jest w formacie ro.llk.stack i domyślnie jest to lista rozdzielana przecinkami w formacie cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable.

Symbole powinny być rzadkie i wystarczająco krótkotrwałe, aby w typowym systemie funkcja była widoczna tylko raz w próbce w okresie limitu czasu ro.llk.stack.timeout_ms (próbki występują co ro.llk.check_ms). Z powodu braku ochrony ABA jest to jedyny sposób na zapobieganie fałszywym wyzwalczeniom. Pod funkcją wywołującą blokadę musi się pojawić funkcja symbolu. Jeśli kłódka znajduje się pod symbolem lub w funkcji symbolu, symbol pojawia się we wszystkich procesach, które zostały zablokowane, a nie tylko w tym, który spowodował zablokowanie.

Pokrycie

Domyślna implementacja llkd nie monitoruje skoków init, [kthreadd] ani [kthreadd]. Aby llkd obejmowało wątki utworzone przez [kthreadd]:

  • Sterowniki nie mogą pozostawać w stanie D przez cały czas.

LUB

  • Sterowniki muszą mieć mechanizmy umożliwiające przywrócenie wątku, jeśli zostanie on przerwany zewnętrznie. Na przykład użyj wait_event_interruptible() zamiast wait_event().

Jeśli spełniony jest jeden z powyższych warunków, listę odrzuconych llkd można dostosować, aby obejmowała komponenty jądra. Sprawdzanie symboli stosu obejmuje dodatkową listę zablokowanych procesów, aby zapobiec naruszeniom zasad bezpieczeństwa w usługach, które blokują operacje ptrace.

Właściwości Androida

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

  • Wartości właściwości o nazwie prop_ms są podawane w milisekundach.
  • Właściwości, które używają przecinka (,) jako separatora list, używają separatora na początku, aby zachować domyślny wpis, a następnie dodawać lub odejmować wpisy za pomocą opcjonalnych prefiksów plusa (+) i minusa (-). W przypadku tych list ciąg znaków false jest równoznaczny z pustą listą, a puste lub brakujące wpisy są zastępowane przez określoną wartość domyślną.

ro.config.low_ram

Urządzenie ma ograniczoną pamięć.

ro.debuggable

Urządzenie jest skonfigurowane pod kątem kompilacji userdebug lub eng.

ro.llk.sysrq_t,

Jeśli właściwość to eng, wartość domyślna nie jest wartością ro.config.low_ram ani ro.debuggable. Jeśli true, zrzut wszystkich wątków (sysrq t).

ro.llk.enable

Zezwól na włączenie demona aktywnej blokady. Domyślna wartość to false.

llk.enable

Sprawdzane w przypadku kompilacji inżynierskich. Domyślna wartość to ro.llk.enable.

ro.khungtask.enable

Zezwalaj na włączanie demona [khungtask]. Wartość domyślna to false.

khungtask.enable

Weryfikowane w przypadku kompilacji inżynierskich. Domyślna wartość to ro.khungtask.enable.

ro.llk.mlockall

Włącz opcję mlockall(). Wartość domyślna to false.

ro.khungtask.timeout

Maksymalny limit czasu: [khungtask]. Domyślnie jest to 12 minut.

ro.llk.timeout_ms

Maksymalny limit czasu D lub Z. Domyślnie jest to 10 minut. Podwójnie zwiększ tę wartość, aby ustawić watchdog alarmu na llkd.

ro.llk.D.timeout_ms

D maksymalny limit czasu. Domyślna wartość to ro.llk.timeout_ms.

ro.llk.Z.timeout_ms

Z maksymalnym limitem czasu. Domyślna wartość to ro.llk.timeout_ms.

ro.llk.stack.timeout_ms

Sprawdzanie maksymalnego limitu czasu dla stałych symboli stosu. Wartość domyślna to ro.llk.timeout_ms. Aktywna tylko w przypadku kompilacji userdebug lub eng.

ro.llk.check_ms

Przykłady wątków dla D lub Z. Domyślnie jest to 2 minuty.

ro.llk.stack

Sprawdza symbole stosu jądra, które w przypadku stałego występowania mogą wskazywać na zablokowanie podsystemu. Domyślnie jest to cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable lista symboli jądra rozdzielona przecinkami. Ta weryfikacja nie wykonuje przekierowania do planowania ABA, z wyjątkiem okresowego sprawdzania co ro.llk_check_ms, więc symbole stosu powinny być wyjątkowo rzadkie i krótkotrwałe (bardzo mało prawdopodobne jest, aby symbol pojawiał się stale we wszystkich próbkach stosu).ro.llk.stack.timeout_ms Sprawdza, czy w rozwiniętym stosie występuje dopasowanie do symbol+0x lub symbol.cfi+0x. Dostępne tylko w przypadku kompilacji userdebug lub eng; ze względów bezpieczeństwa kompilacje przeznaczone dla użytkowników mają ograniczone uprawnienia, które uniemożliwiają wykonanie tego sprawdzenia.

ro.llk.blacklist.process,

llkd nie obserwuje określonych procesów. Wartość domyślna to 0,1,2 (kernel, init i [kthreadd]) oraz nazwy procesów init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1]. Proces może być odwołaniem do comm, cmdline lub pid. Automatyczny domyślny adres IP może być większy niż obecny maksymalny rozmiar usługi, który wynosi 92.

ro.llk.blacklist.parent,

llkd nie monitoruje procesów, które mają określone elementy nadrzędne. Wartość domyślna to 0,2,adbd&[setsid] (kernel, [kthreadd] i adbd tylko w przypadku procesów zombiesetsid). Znak ampersand (&) oznacza, że proces nadrzędny jest ignorowany tylko w połączeniu z docelowym procesem podrzędnym. Wybrano znak „&”, ponieważ nigdy nie występuje on w nazwie procesu. Jednak w powłoce znak „setprop” wymaga ucieczki lub umieszczenia w cudzysłowie, podczas gdy plik setprop, w którym jest on zwykle używany, nie ma tego problemu.init rc Proces nadrzędny lub docelowy może być odwołaniem comm, cmdline lub pid.

ro.llk.blacklist.uid

llkd nie monitoruje procesów, które pasują do określonych identyfikatorów UID. Lista numerów lub nazw UIS rozdzielona przecinkami. Wartość domyślna to pusty ciąg lub false.

ro.llk.blacklist.process.stack

llkd nie monitoruje określonego podzbioru procesów pod kątem podpisów stosu blokady na żywo. Wartość domyślna to nazwy procesów.init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd Zapobiega naruszeniu zasad bezpieczeństwa związanemu z procesami, które blokują ptrace (ponieważ nie można ich sprawdzić). Aktywna tylko w przypadku kompilacji userdebug i eng. Szczegółowe informacje o typach kompilacji znajdziesz w artykule Kompilowanie aplikacji na Androida.

Kwestie architektoniczne

  • Właściwości mogą zawierać maksymalnie 92 znaki (jest to jednak 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 za często wywołuje kod sterownika, który zbyt długo pozostaje w stanie D. Przejście na tryb S spowoduje, że zadania będą mogły zostać zakończone (i w razie potrzeby przywrócone przez kierowców).

Interfejs biblioteki (opcjonalnie)

Opcjonalnie możesz włączyć llkd do innego demona z przywilejami, korzystając z tego 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 nazwa wątku zostanie podana, wątek pojawi się automatycznie. W przeciwnym razie element wywołujący musi wywołać funkcję llkCheckMilliseconds w swojej głównej pętli. Funkcja zwraca okres czasu do następnego oczekiwanego wywołania tego modułu obsługi.