Démon Android Live-LocK (llkd)

Android 10 inclut le démon Android Live-LocK ( llkd ), conçu pour détecter et atténuer les blocages du noyau. Le composant llkd fournit une implémentation autonome par défaut, mais vous pouvez également intégrer le code llkd dans un autre service, soit dans le cadre de la boucle principale, soit en tant que thread séparé.

Scénarios de détection

Le llkd propose deux scénarios de détection : état D ou Z persistant et signature de pile persistante.

État D ou Z persistant

Si un thread est dans l'état D (veille ininterrompue) ou Z (zombie) sans progression pendant plus de ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms , le llkd tue le processus (ou le processus parent). ). Si une analyse ultérieure montre que le même processus continue d'exister, le llkd confirme une condition de verrouillage en direct et panique le noyau de manière à fournir le rapport de bogue le plus détaillé pour la condition.

Le llkd comprend un chien de garde automatique qui déclenche une alarme si llkd se bloque ; le chien de garde est le double du temps prévu pour parcourir la boucle principale et l'échantillonnage est tous ro.llk_sample_ms .

Signature de pile persistante

Pour les versions userdebug, le llkd peut détecter les verrous actifs du noyau en utilisant la vérification persistante de la signature de la pile. Si un thread dans n'importe quel état sauf Z a un symbole de noyau ro.llk.stack persistant qui est signalé depuis plus longtemps que ro.llk.timeout_ms ou ro.llk.stack.timeout_ms , le llkd tue le processus (même s'il y a un transfert vers l'avant). progression de la planification). Si une analyse ultérieure montre que le même processus continue d'exister, le llkd confirme une condition de verrouillage en direct et panique le noyau de manière à fournir le rapport de bogue le plus détaillé pour la condition.

La vérification lldk persiste en continu lorsque la condition de verrouillage en direct existe et recherche les chaînes composées " symbol+0x" ou " symbol.cfi+0x" dans le fichier /proc/pid/stack sous Linux. La liste des symboles se trouve dans ro.llk.stack et est par défaut la liste séparée par des virgules de " cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable ".

Les symboles doivent être suffisamment rares et de courte durée pour que sur un système typique, la fonction ne soit vue qu'une seule fois dans un échantillon pendant le délai d'expiration de ro.llk.stack.timeout_ms (les échantillons se produisent tous ro.llk.check_ms ). En raison du manque de protection ABA, c’est le seul moyen d’éviter un faux déclenchement. Le symbole fonction doit apparaître en dessous de la fonction appelant le verrou qui pourrait rivaliser. Si le verrou se trouve en dessous ou dans la fonction symbole, le symbole apparaît dans tous les processus concernés, pas seulement celui qui a provoqué le blocage.

Couverture

L'implémentation par défaut de llkd ne surveille pas les spawns init , [kthreadd] ou [kthreadd] . Pour que llkd couvre les threads générés par [kthreadd] :

  • Les conducteurs ne doivent pas rester dans un état D persistant,

OU

  • Les pilotes doivent disposer de mécanismes pour récupérer le thread s'il est supprimé en externe. Par exemple, utilisez wait_event_interruptible() au lieu de wait_event() .

Si l'une des conditions ci-dessus est remplie, la liste noire llkd peut être ajustée pour couvrir les composants du noyau. La vérification des symboles de pile implique une liste noire de processus supplémentaire pour éviter les violations de politique de sécurité sur les services qui bloquent les opérations ptrace .

Propriétés Android

Le llkd répond à plusieurs propriétés Android (répertoriées ci-dessous).

  • Les propriétés nommées prop_ms sont en millisecondes.
  • Les propriétés qui utilisent un séparateur virgule (,) pour les listes utilisent un séparateur de début pour conserver l'entrée par défaut, puis ajoutent ou soustraient des entrées avec les préfixes plus (+) et moins (-) facultatifs respectivement. Pour ces listes, la chaîne "false" est synonyme de liste vide, et les entrées vides ou manquantes recourent à la valeur par défaut spécifiée.

ro.config.low_ram

L'appareil est configuré avec une mémoire limitée.

ro.déboguable

L'appareil est configuré pour userdebug ou eng build.

ro.llk.sysrq_t

Si la propriété est "eng", la valeur par défaut n'est pas ro.config.low_ram ou ro.debuggable . Si c'est vrai, videz tous les threads ( sysrq t ).

ro.llk.activer

Autoriser l'activation du démon live-lock. La valeur par défaut est fausse.

llk.activer

Évalué pour les versions anglaises. La valeur par défaut est ro.llk.enable .

ro.khungtask.enable

Autoriser le démon [khungtask] à être activé. La valeur par défaut est fausse.

khungtask.activer

Évalué pour les versions anglaises. La valeur par défaut est ro.khungtask.enable .

ro.llk.mlockall

Activer l'appel à mlockall() . La valeur par défaut est fausse.

ro.khungtask.timeout

[khungtask] délai maximum. La valeur par défaut est de 12 minutes.

ro.llk.timeout_ms

Délai maximum D ou Z. La valeur par défaut est de 10 minutes. Doublez cette valeur pour définir le chien de garde d'alarme pour llkd .

ro.llk.D.timeout_ms

D délai maximum. La valeur par défaut est ro.llk.timeout_ms .

ro.llk.Z.timeout_ms

Délai maximum Z. La valeur par défaut est ro.llk.timeout_ms .

ro.llk.stack.timeout_ms

Vérifie la durée maximale des symboles de pile persistants. La valeur par défaut est ro.llk.timeout_ms . Actif uniquement sur les builds userdebug ou eng .

ro.llk.check_ms

Exemples de fils de discussion pour D ou Z. La valeur par défaut est de deux minutes.

ro.llk.stack

Vérifie les symboles de pile du noyau qui, s'ils sont présents de manière persistante, peuvent indiquer qu'un sous-système est verrouillé. La valeur par défaut est cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable liste de symboles du noyau séparés par des virgules. La vérification n'effectue pas de planification avancée ABA, sauf en interrogeant chaque ro.llk_check_ms pendant la période ro.llk.stack.timeout_ms , les symboles de pile doivent donc être exceptionnellement rares et éphémères (il est très peu probable qu'un symbole apparaisse de manière persistante dans tous les fichiers). échantillons de la pile). Vérifie une correspondance pour " symbol+0x" ou " symbol.cfi+0x" dans l'expansion de la pile. Disponible uniquement sur les builds userdebug ou eng ; les problèmes de sécurité sur les builds utilisateur entraînent des privilèges limités qui empêchent cette vérification.

ro.llk.blacklist.process

Le llkd ne surveille pas les processus spécifiés. La valeur par défaut est 0,1,2 ( kernel , init et [kthreadd] ) plus les noms de processus init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1] . Un processus peut être une référence comm , cmdline ou pid . Une valeur par défaut automatisée peut être supérieure à la taille de propriété maximale actuelle de 92.

ro.llk.liste noire.parent

Le llkd ne surveille pas les processus qui ont le(s) parent(s) spécifié(s). La valeur par défaut est 0,2,adbd&[setsid] ( kernel , [kthreadd] et adbd uniquement pour zombie setsid ). Un séparateur esperluette (&) spécifie que le parent est ignoré uniquement en combinaison avec le processus enfant cible. L'esperluette a été sélectionnée car elle ne fait jamais partie d'un nom de processus ; cependant, un setprop dans le shell nécessite que l'esperluette soit échappée ou citée, bien que le fichier init rc dans lequel cela est normalement spécifié n'ait pas ce problème. Un processus parent ou cible peut être une référence comm , cmdline ou pid .

ro.llk.blacklist.uid

Le llkd ne surveille pas les processus qui correspondent au(x) uid(s) spécifié(s). Liste de numéros ou de noms d'uid séparés par des virgules. La valeur par défaut est vide ou fausse.

ro.llk.blacklist.process.stack

llkd ne surveille pas le sous-ensemble spécifié de processus pour les signatures de pile de verrouillage en direct. La valeur par défaut est les noms de processus init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd . Empêche la violation de la politique de sécurité associée aux processus qui bloquent ptrace (car ceux-ci ne peuvent pas être vérifiés). Actif uniquement sur les builds userdebug et eng . Pour plus de détails sur les types de build, reportez-vous à Building Android .

Préoccupations architecturales

  • Les propriétés sont limitées à 92 caractères (ceci est cependant ignoré pour les valeurs par défaut définies dans le fichier include/llkd.h des sources).
  • Le démon intégré [khungtask] est trop générique et se déclenche trop sur le code du pilote qui se trouve dans l'état D. Passer à S rendrait les tâches tuables (et ressuscitables par les pilotes si nécessaire).

Interface de bibliothèque (facultatif)

Vous pouvez éventuellement incorporer le llkd dans un autre démon privilégié en utilisant l'interface C suivante du composant libllkd :

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

Si un nom de thread est fourni, un thread apparaît automatiquement, sinon l'appelant doit appeler llkCheckMilliseconds dans sa boucle principale. La fonction renvoie la période de temps avant le prochain appel attendu à ce gestionnaire.