Android Live Lock-Daemon (LKD)

Android 10 enthält den Android-Live-Lock-Daemon (llkd), der Kernel-Deadlocks erkennen und beheben soll. Die llkd-Komponente bietet eine standardmäßige eigenständige Implementierung. Alternativ können Sie den llkd-Code in einen anderen Dienst einbinden, entweder als Teil der Hauptschleife oder als separaten Thread.

Erkennungsszenarien

Für die llkd gibt es zwei Erkennungsszenarien: Dauerhafter D‑ oder Z‑Zustand und dauerhafte Stacksignatur.

Dauerhafter D- oder Z-Zustand

Wenn sich ein Thread im Status „D“ (unveränderlicher Ruhezustand) oder „Z“ (Zombie) befindet und länger als ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms Minuten lang keine Fortschritte macht, beendet llkd den Prozess (oder übergeordneten Prozess). Wenn bei einem nachfolgenden Scan derselbe Prozess weiterhin vorhanden ist, bestätigt llkd eine Live-Sperrung und versetzt den Kernel in Panik, sodass der detaillierteste Fehlerbericht für die Situation erstellt wird.

Der llkd enthält einen Selbst-Watchdog, der Alarm schlägt, wenn llkd hängt. Der Watchdog ist doppelt so lang wie die erwartete Zeit, die für die Ausführung des Mainloops erforderlich ist, und die Stichprobenerhebung erfolgt alle ro.llk_sample_ms.

Persistente Stacksignatur

Bei Releases vom Typ „userdebug“ kann die llkd Kernel-Live-Sperrungen mithilfe einer persistenten Stacksignaturprüfung erkennen. Wenn ein Thread in einem beliebigen Status außer Z ein dauerhaft aufgeführtes ro.llk.stack-Kernelsymbol hat, das länger als ro.llk.timeout_ms oder ro.llk.stack.timeout_ms gemeldet wird, beendet llkd den Prozess, auch wenn die Vorwärtsplanung fortschreitet. Wenn bei einem nachfolgenden Scan derselbe Prozess weiterhin vorhanden ist, bestätigt llkd eine Live-Sperrung und versetzt den Kernel in Panik, um den detailliertesten Fehlerbericht für die Situation zu erhalten.

Die lldk-Prüfung wird kontinuierlich beibehalten, wenn die Bedingung für die Live-Sperre erfüllt ist, und sucht unter Linux in der Datei /proc/pid/stack nach den zusammengesetzten Strings symbol+0x oder symbol.cfi+0x. Die Liste der Symbole befindet sich in ro.llk.stack und ist standardmäßig eine durch Kommas getrennte Liste von cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable.

Symbole sollten so selten und kurzlebig sein, dass die Funktion in einem typischen System über das Zeitlimit von ro.llk.stack.timeout_ms nur einmal in einer Stichprobe zu sehen ist (Beispiele erscheinen alle ro.llk.check_ms). Aufgrund des fehlenden ABA-Schutzes ist dies die einzige Möglichkeit, einen falschen Trigger zu verhindern. Die Symbolfunktion muss unter der Funktion angezeigt werden, die die fragliche Sperre aufruft. Wenn sich das Schloss unterhalb oder in der Symbolfunktion befindet, wird das Symbol in allen betroffenen Prozessen angezeigt, nicht nur in dem, der das Lockup verursacht hat.

Abdeckung

Die Standardimplementierung von llkd überwacht keine Spawns vom Typ init, [kthreadd] oder [kthreadd]. Für llkd, um [kthreadd]-erzeugte Threads abzudecken:

  • Fahrer dürfen sich nicht dauerhaft im D-Status befinden.

ODER

  • Treiber müssen Mechanismen zum Wiederherstellen des Threads haben, falls er extern beendet wird. Verwenden Sie z. B. wait_event_interruptible() statt wait_event().

Wenn eine der oben genannten Bedingungen erfüllt ist, kann die llkd-Deaktivierungsliste so angepasst werden, dass sie Kernelkomponenten umfasst. Die Stacksymbolprüfung umfasst eine zusätzliche Prozess-Denylist, um Verstöße gegen die SE-Richtlinie bei Diensten zu verhindern, die ptrace-Vorgänge blockieren.

Android-Properties

Die llkd reagiert auf mehrere Android-Properties (siehe unten).

  • Für Properties mit dem Namen prop_ms werden Millisekunden verwendet.
  • Bei Eigenschaften, in denen für Listen ein Komma (,) als Trennzeichen verwendet wird, wird ein vorangestelltes Trennzeichen verwendet, um den Standardeintrag beizubehalten. Anschließend werden Einträge mit optionalen Pluszeichen (+) bzw. Minus (-) hinzugefügt oder subtrahiert. Bei diesen Listen steht der String false für eine leere Liste. Leere oder fehlende Einträge werden durch den angegebenen Standardwert ersetzt.

ro.config.low_ram

Das Gerät ist mit begrenztem Arbeitsspeicher konfiguriert.

ro.debuggable

Das Gerät ist für UserDebug oder Engine-Build konfiguriert.

ro.llk.sysrq_t

Wenn die Property eng ist, ist der Standardwert nicht ro.config.low_ram oder ro.debuggable. Wenn true, werden alle Threads gedumpt (sysrq t).

ro.llk.enable

Aktivieren des Live-Lock-Daemons zulassen. Die Standardeinstellung ist false.

llk.enable

Für Entwickler-Builds bewertet. Die Standardeinstellung ist ro.llk.enable.

ro.khungtask.enable

Aktivieren des [khungtask]-Daemons zulassen. Der Standardwert ist false.

khungtask.enable

Wird für eng-Builds ausgewertet. Die Standardeinstellung ist ro.khungtask.enable.

ro.llk.mlockall

Aktivieren Sie den Aufruf von mlockall(). Der Standardwert ist false.

ro.khungtask.timeout

[khungtask] als maximales Zeitlimit. Der Standardwert ist 12 Minuten.

ro.llk.timeout_ms

D oder Z für das maximale Zeitlimit. Der Standardwert ist 10 Minuten. Verdoppeln Sie diesen Wert, um den Alarm-Watchdog für llkd festzulegen.

ro.llk.D.timeout_ms

D Maximales Zeitlimit Die Standardeinstellung ist ro.llk.timeout_ms.

ro.llk.Z.timeout_ms

Z maximales Zeitlimit. Die Standardeinstellung ist ro.llk.timeout_ms.

ro.llk.stack.timeout_ms

Prüft, ob das Zeitlimit für persistente Stacksymbole überschritten wurde. Der Standardwert ist ro.llk.timeout_ms. Nur bei userdebug- oder eng-Builds aktiv.

ro.llk.check_ms

Musterbeispiele für D- oder Z-Glieder Der Standardwert ist zwei Minuten.

ro.llk.stack

Prüft auf Kernel-Stack-Symbole, die darauf hinweisen können, dass ein Subsystem gesperrt ist. Der Standardwert ist die durch Kommas getrennte Liste cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable von Kernel-Symbolen. Die Prüfung führt keine Forward-Planung für ABA aus, außer dass alle ro.llk_check_ms im Zeitraum ro.llk.stack.timeout_ms abgefragt werden. Daher sollten Stapelsymbole sehr selten und kurzlebig sein (es ist sehr unwahrscheinlich, dass ein Symbol dauerhaft in allen Stichproben des Stacks auftaucht). Prüft bei der Stapelerweiterung auf Übereinstimmung mit symbol+0x oder symbol.cfi+0x. Nur für Userdebug- oder eng-Builds verfügbar: Sicherheitsbedenken bei User-Builds führen zu eingeschränkten Berechtigungen, die diese Prüfung verhindern.

ro.llk.blacklist.process

llkd überwacht die angegebenen Prozesse nicht. Der Standardwert ist 0,1,2 (kernel, init und [kthreadd]) plus die Prozessnamen init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1]. Ein Prozess kann eine comm-, cmdline- oder pid-Referenz sein. Ein automatischer Standard kann größer als die aktuelle maximale Unterkunftsgröße von 92 sein.

ro.llk.blacklist.parent

Die llkd überwacht keine Prozesse mit den angegebenen übergeordneten Elementen. Der Standardwert ist 0,2,adbd&[setsid] (kernel, [kthreadd] und adbd nur für Zombiesetsid). Ein kaufmännisches Und-Zeichen (&) gibt an, dass das übergeordnete Element nur in Kombination mit dem Zielunterprozess ignoriert wird. Das Andzeichen wurde ausgewählt, da es nie Teil eines Prozessnamens ist. Bei einer setprop in der Shell muss das Andzeichen jedoch mit einem Escape-Zeichen oder in Anführungszeichen gesetzt werden. In der init rc-Datei, in der dies normalerweise angegeben wird, tritt dieses Problem nicht auf. Ein übergeordneter oder Zielprozess kann eine comm-, cmdline- oder pid-Referenz sein.

ro.llk.blacklist.uid

Die llkd überwacht keine Prozesse, die mit den angegebenen UIDs übereinstimmen. Durch Kommas getrennte Liste von UIS-Nummern oder ‑Namen. Der Standardwert ist leer oder false.

ro.llk.blacklist.process.stack

Der llkd überwacht die angegebene Teilmenge von Prozessen nicht auf Live-Sperrbalken-Stacksignaturen. Standardmäßig werden Prozessnameninit,lmkd.llkd,llkd,keystore,ueventd,apexd,logd verwendet. Verhindert den Sepolicy-Verstoß, der mit Prozessen verbunden ist, die ptrace blockieren, da diese nicht geprüft werden können. Nur bei userdebug- und eng-Builds aktiv. Weitere Informationen zu Build-Typen finden Sie unter Android erstellen.

Architekturbezogene Bedenken

  • Attribute sind auf 92 Zeichen beschränkt. Bei Standardeinstellungen, die in der Datei include/llkd.h in den Quellen definiert sind, wird dies jedoch ignoriert.
  • Der integrierte [khungtask]-Daemon ist zu allgemein und löst bei Treibercode aus, der zu viel im D-Zustand verharrt. Wenn Sie zu „S“ wechseln, können Aufgaben beendet und bei Bedarf von Treibern wiederhergestellt werden.

Bibliotheksoberfläche (optional)

Optional können Sie llkd mithilfe der folgenden C-Schnittstelle aus der libllkd-Komponente in einen anderen privilegierten Daemon einbinden:

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

Wenn ein Threadname angegeben wird, wird ein Thread automatisch erstellt. Andernfalls muss der Aufrufer llkCheckMilliseconds in seiner Hauptschleife aufrufen. Die Funktion gibt den Zeitraum vor dem nächsten erwarteten Aufruf an diesen Handler zurück.